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' ...@@ -3,13 +3,13 @@ import { bioEnititiesResultListSelector } from '@/redux/drawer/drawer.selectors'
import { DrawerHeadingBackwardButton } from '@/shared/DrawerHeadingBackwardButton'; import { DrawerHeadingBackwardButton } from '@/shared/DrawerHeadingBackwardButton';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { displayGroupedSearchResults } from '@/redux/drawer/drawer.slice'; 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'; import { BioEntitiesPinsList } from './BioEntitiesPinsList';
export const BioEntitiesResultsList = (): JSX.Element => { export const BioEntitiesResultsList = (): JSX.Element => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const bioEntityData = useAppSelector(bioEnititiesResultListSelector); const bioEntityData = useAppSelector(bioEnititiesResultListSelector);
const searchValue = useAppSelector(searchValueSelector); const mapName = useAppSelector(currentModelNameSelector);
const navigateToGroupedSearchResults = (): void => { const navigateToGroupedSearchResults = (): void => {
dispatch(displayGroupedSearchResults()); dispatch(displayGroupedSearchResults());
...@@ -17,11 +17,9 @@ export const BioEntitiesResultsList = (): JSX.Element => { ...@@ -17,11 +17,9 @@ export const BioEntitiesResultsList = (): JSX.Element => {
return ( return (
<div> <div>
<DrawerHeadingBackwardButton <DrawerHeadingBackwardButton backwardFunction={navigateToGroupedSearchResults}>
title="BioEntity" {mapName}
value={searchValue} </DrawerHeadingBackwardButton>
backwardFunction={navigateToGroupedSearchResults}
/>
<BioEntitiesPinsList bioEnititesPins={bioEntityData} /> <BioEntitiesPinsList bioEnititesPins={bioEntityData} />
</div> </div>
); );
......
import { BioEntitiesAccordion } from '@/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion'; import { BioEntitiesAccordion } from '@/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion';
import { DrugsAccordion } from '@/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/DrugsAccordion'; import { DrugsAccordion } from '@/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/DrugsAccordion';
import { ChemicalsAccordion } from '@/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/ChemicalsAccordion'; import { ChemicalsAccordion } from '@/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/ChemicalsAccordion';
import { MirnaAccordion } from '@/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/MirnaAccordion';
import { Accordion } from '@/shared/Accordion'; import { Accordion } from '@/shared/Accordion';
export const CLOSE_BUTTON_ROLE = 'close-drawer-button'; export const CLOSE_BUTTON_ROLE = 'close-drawer-button';
...@@ -14,7 +13,6 @@ export const GroupedSearchResults = (): JSX.Element => { ...@@ -14,7 +13,6 @@ export const GroupedSearchResults = (): JSX.Element => {
<BioEntitiesAccordion /> <BioEntitiesAccordion />
<DrugsAccordion /> <DrugsAccordion />
<ChemicalsAccordion /> <ChemicalsAccordion />
<MirnaAccordion />
</Accordion> </Accordion>
</div> </div>
</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 { chemicalsFixture } from '@/models/fixtures/chemicalsFixture';
import { drugsFixture } from '@/models/fixtures/drugFixtures'; import { drugsFixture } from '@/models/fixtures/drugFixtures';
import { mirnasFixture } from '@/models/fixtures/mirnasFixture';
import { StoreType } from '@/redux/store'; import { StoreType } from '@/redux/store';
import { import {
InitialStoreState, InitialStoreState,
...@@ -22,12 +21,6 @@ const CHEMICALS_PINS_LIST = chemicalsFixture.map(chemical => ({ ...@@ -22,12 +21,6 @@ const CHEMICALS_PINS_LIST = chemicalsFixture.map(chemical => ({
data: chemical, data: chemical,
})); }));
const MIRNA_PINS_LIST = mirnasFixture.map(mirna => ({
id: mirna.id,
name: mirna.name,
data: mirna,
}));
const renderComponent = ( const renderComponent = (
pinsList: PinItem[], pinsList: PinItem[],
type: PinTypeWithNone, type: PinTypeWithNone,
...@@ -68,11 +61,6 @@ describe('PinsList - component ', () => { ...@@ -68,11 +61,6 @@ describe('PinsList - component ', () => {
expect(screen.getByTestId('accordions-details')).toBeInTheDocument(); 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', () => { it('should not display list of bio enities when bioEntity is searched', () => {
renderComponent([], 'bioEntity'); renderComponent([], 'bioEntity');
......
...@@ -38,16 +38,6 @@ export const PinsList = ({ pinsList, type }: PinsListProps): JSX.Element => { ...@@ -38,16 +38,6 @@ export const PinsList = ({ pinsList, type }: PinsListProps): JSX.Element => {
</ul> </ul>
</div> </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': case 'none':
return <div />; return <div />;
default: default:
......
import { Chemical, Drug, Mirna } from '@/types/models'; import { Chemical, Drug } from '@/types/models';
import { PinType } from '@/types/pin'; import { PinType } from '@/types/pin';
export type PinItem = { export type PinItem = {
id: string | number; id: string | number;
name: string; name: string;
data: Drug | Chemical | Mirna; data: Drug | Chemical;
}; };
export type PinTypeWithNone = PinType | 'none'; export type PinTypeWithNone = PinType | 'none';
...@@ -4,13 +4,13 @@ import { twMerge } from 'tailwind-merge'; ...@@ -4,13 +4,13 @@ import { twMerge } from 'tailwind-merge';
import { PinTypeWithNone } from '../PinsList.types'; import { PinTypeWithNone } from '../PinsList.types';
import { getPinColor } from './PinsListItem.component.utils'; import { getPinColor } from './PinsListItem.component.utils';
interface MirnaPinsListItemProps { interface PinsListItemProps {
name: string; name: string;
type: PinTypeWithNone; type: PinTypeWithNone;
pin: PinDetailsItem; pin: PinDetailsItem;
} }
export const PinsListItem = ({ name, type, pin }: MirnaPinsListItemProps): JSX.Element => { export const PinsListItem = ({ name, type, pin }: PinsListItemProps): JSX.Element => {
return ( 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="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"> <div className="flex w-full flex-row items-center gap-2">
......
...@@ -5,7 +5,6 @@ export const getPinColor = (type: PinTypeWithNone): string => { ...@@ -5,7 +5,6 @@ export const getPinColor = (type: PinTypeWithNone): string => {
bioEntity: 'fill-primary-500', bioEntity: 'fill-primary-500',
drugs: 'fill-orange', drugs: 'fill-orange',
chemicals: 'fill-purple', chemicals: 'fill-purple',
mirna: 'fill-pink',
none: 'none', none: 'none',
}; };
......
...@@ -59,8 +59,8 @@ describe('ResultsList - component ', () => { ...@@ -59,8 +59,8 @@ describe('ResultsList - component ', () => {
it('should render results and navigation panel', () => { it('should render results and navigation panel', () => {
renderComponent(INITIAL_STATE); renderComponent(INITIAL_STATE);
expect(screen.getByText('drugs:')).toBeInTheDocument(); const headingText = screen.getByTestId('drawer-heading-text');
expect(screen.getByText('aspirin')).toBeInTheDocument(); expect(headingText.textContent).toBe('drugs');
const fristDrugName = drugsFixture[0].targets[0].name; const fristDrugName = drugsFixture[0].targets[0].name;
const secondDrugName = drugsFixture[0].targets[1].name; const secondDrugName = drugsFixture[0].targets[1].name;
......
...@@ -3,14 +3,12 @@ import { resultListSelector, stepTypeDrawerSelector } from '@/redux/drawer/drawe ...@@ -3,14 +3,12 @@ import { resultListSelector, stepTypeDrawerSelector } from '@/redux/drawer/drawe
import { DrawerHeadingBackwardButton } from '@/shared/DrawerHeadingBackwardButton'; import { DrawerHeadingBackwardButton } from '@/shared/DrawerHeadingBackwardButton';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { displayGroupedSearchResults } from '@/redux/drawer/drawer.slice'; import { displayGroupedSearchResults } from '@/redux/drawer/drawer.slice';
import { searchValueSelector } from '@/redux/search/search.selectors';
import { PinsList } from './PinsList'; import { PinsList } from './PinsList';
export const ResultsList = (): JSX.Element => { export const ResultsList = (): JSX.Element => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const data = useAppSelector(resultListSelector); const data = useAppSelector(resultListSelector);
const stepType = useAppSelector(stepTypeDrawerSelector); const stepType = useAppSelector(stepTypeDrawerSelector);
const searchValue = useAppSelector(searchValueSelector);
const navigateToGroupedSearchResults = (): void => { const navigateToGroupedSearchResults = (): void => {
dispatch(displayGroupedSearchResults()); dispatch(displayGroupedSearchResults());
...@@ -18,11 +16,11 @@ export const ResultsList = (): JSX.Element => { ...@@ -18,11 +16,11 @@ export const ResultsList = (): JSX.Element => {
return ( return (
<div> <div>
<DrawerHeadingBackwardButton <DrawerHeadingBackwardButton backwardFunction={navigateToGroupedSearchResults}>
title={stepType} <span className="capitalize" data-testid="drawer-heading-text">
value={searchValue} {stepType}
backwardFunction={navigateToGroupedSearchResults} </span>
/> </DrawerHeadingBackwardButton>
{data && <PinsList pinsList={data} type={stepType} />} {data && <PinsList pinsList={data} type={stepType} />}
</div> </div>
); );
......
import { BIO_ENTITY, DRUGS_CHEMICALS_MIRNA } from '@/constants'; import { BIO_ENTITY, DRUGS_CHEMICALS } from '@/constants';
import { STEP } from '@/constants/searchDrawer'; import { STEP } from '@/constants/searchDrawer';
import { import {
currentStepDrawerStateSelector, currentStepDrawerStateSelector,
...@@ -16,7 +16,7 @@ export const SearchDrawerWrapper = (): JSX.Element => { ...@@ -16,7 +16,7 @@ export const SearchDrawerWrapper = (): JSX.Element => {
const stepType = useSelector(stepTypeDrawerSelector); const stepType = useSelector(stepTypeDrawerSelector);
const isBioEntityType = stepType === BIO_ENTITY; const isBioEntityType = stepType === BIO_ENTITY;
const isChemicalsDrugsOrMirnaType = DRUGS_CHEMICALS_MIRNA.includes(stepType); const isChemicalsOrDrugsType = DRUGS_CHEMICALS.includes(stepType);
return ( return (
<> <>
...@@ -31,8 +31,8 @@ export const SearchDrawerWrapper = (): JSX.Element => { ...@@ -31,8 +31,8 @@ export const SearchDrawerWrapper = (): JSX.Element => {
<BioEntitiesResultsList /> <BioEntitiesResultsList />
</div> </div>
)} )}
{/* 2nd step for drugs,chemicals,mirna */} {/* 2nd step for drugs,chemicals */}
{currentStep === STEP.SECOND && isChemicalsDrugsOrMirnaType && ( {currentStep === STEP.SECOND && isChemicalsOrDrugsType && (
<div data-testid="search-second-step"> <div data-testid="search-second-step">
<ResultsList /> <ResultsList />
</div> </div>
......
...@@ -29,7 +29,7 @@ describe('getBioEntitiesFeatures - subUtil', () => { ...@@ -29,7 +29,7 @@ describe('getBioEntitiesFeatures - subUtil', () => {
const bioEntities = bioEntititesContent.map(({ bioEntity }) => bioEntity); const bioEntities = bioEntititesContent.map(({ bioEntity }) => bioEntity);
const pointToProjection = getPointToProjection(Wrapper); 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 => { it.each(pinTypes)('should return array of instances of Feature with Style type=%s', type => {
const result = getBioEntitiesFeatures(bioEntities, { const result = getBioEntitiesFeatures(bioEntities, {
......
...@@ -38,7 +38,7 @@ describe('getBioEntitySingleFeature - subUtil', () => { ...@@ -38,7 +38,7 @@ describe('getBioEntitySingleFeature - subUtil', () => {
const pointToProjection = getPointToProjection(Wrapper); const pointToProjection = getPointToProjection(Wrapper);
const value = 1448; 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 => { it.each(pinTypes)('should return instance of Feature with Style type=%s', type => {
const result = getBioEntitySingleFeature(bioEntity, { const result = getBioEntitySingleFeature(bioEntity, {
......
...@@ -12,7 +12,6 @@ export const PINS_COLORS: Record<PinType, string> = { ...@@ -12,7 +12,6 @@ export const PINS_COLORS: Record<PinType, string> = {
drugs: '#F48C41', drugs: '#F48C41',
chemicals: '#640CE3', chemicals: '#640CE3',
bioEntity: '#106AD7', bioEntity: '#106AD7',
mirna: '#F1009F',
}; };
export const LINE_COLOR = '#00AAFF'; export const LINE_COLOR = '#00AAFF';
......
...@@ -6,4 +6,4 @@ export const BASE_NEW_API_URL = process.env.NEXT_PUBLIC_BASE_NEW_API_URL || ''; ...@@ -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 PROJECT_ID = process.env.NEXT_PUBLIC_PROJECT_ID || '';
export const ZOD_SEED = parseInt(process.env.ZOD_SEED || '123', 10); export const ZOD_SEED = parseInt(process.env.ZOD_SEED || '123', 10);
export const BIO_ENTITY = 'bioEntity'; 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', () => { ...@@ -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', () => { it('should return url string for bio entity content', () => {
expect( expect(
apiPath.getBioEntityContentsStringWithQuery({ searchQuery: 'park7', isPerfectMatch: false }), apiPath.getBioEntityContentsStringWithQuery({ searchQuery: 'park7', isPerfectMatch: false }),
......