Skip to content
Snippets Groups Projects
Commit d4c2c2f3 authored by Adrian Orłów's avatar Adrian Orłów
Browse files

feat: add drugs chemicals search for target

parent 6cce9291
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...,!130feat: add drugs chemicals search for target (MIN-187)
Pipeline #86199 passed
Showing
with 717 additions and 53 deletions
/* eslint-disable no-magic-numbers */ /* eslint-disable no-magic-numbers */
import { import { FIRST_ARRAY_ELEMENT } from '@/constants/common';
InitialStoreState,
getReduxWrapperWithStore,
} from '@/utils/testing/getReduxWrapperWithStore';
import { render, screen } from '@testing-library/react';
import { DRAWER_INITIAL_STATE } from '@/redux/drawer/drawer.constants';
import { StoreType } from '@/redux/store';
import { import {
bioEntitiesContentFixture, bioEntitiesContentFixture,
bioEntityContentFixture, bioEntityContentFixture,
} from '@/models/fixtures/bioEntityContentsFixture'; } from '@/models/fixtures/bioEntityContentsFixture';
import { FIRST_ARRAY_ELEMENT } from '@/constants/common'; import { MODELS_MOCK_SHORT } from '@/models/mocks/modelsMock';
import { import {
BIOENTITY_INITIAL_STATE_MOCK, BIOENTITY_INITIAL_STATE_MOCK,
BIO_ENTITY_LINKING_TO_SUBMAP_DATA_MOCK, BIO_ENTITY_LINKING_TO_SUBMAP_DATA_MOCK,
} from '@/redux/bioEntity/bioEntity.mock'; } from '@/redux/bioEntity/bioEntity.mock';
import { MODELS_MOCK_SHORT } from '@/models/mocks/modelsMock'; import { DRAWER_INITIAL_STATE } from '@/redux/drawer/drawer.constants';
import { MODELS_INITIAL_STATE_MOCK } from '@/redux/models/models.mock'; import { MODELS_INITIAL_STATE_MOCK } from '@/redux/models/models.mock';
import { INITIAL_STORE_STATE_MOCK } from '@/redux/root/root.fixtures';
import { AppDispatch, RootState } from '@/redux/store';
import { getReduxStoreWithActionsListener } from '@/utils/testing/getReduxStoreActionsListener';
import { InitialStoreState } from '@/utils/testing/getReduxWrapperWithStore';
import { act, render, screen } from '@testing-library/react';
import { MockStoreEnhanced } from 'redux-mock-store';
import { BioEntityDrawer } from './BioEntityDrawer.component'; import { BioEntityDrawer } from './BioEntityDrawer.component';
const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => { const renderComponent = (
const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState); initialStoreState: InitialStoreState = {},
): { store: MockStoreEnhanced<Partial<RootState>, AppDispatch> } => {
const { Wrapper, store } = getReduxStoreWithActionsListener({
...INITIAL_STORE_STATE_MOCK,
...initialStoreState,
});
return ( return (
render( render(
...@@ -80,6 +85,8 @@ describe('BioEntityDrawer - component', () => { ...@@ -80,6 +85,8 @@ describe('BioEntityDrawer - component', () => {
...DRAWER_INITIAL_STATE, ...DRAWER_INITIAL_STATE,
bioEntityDrawerState: { bioEntityDrawerState: {
bioentityId: bioEntity.id, bioentityId: bioEntity.id,
drugs: {},
chemicals: {},
}, },
}, },
}); });
...@@ -114,6 +121,8 @@ describe('BioEntityDrawer - component', () => { ...@@ -114,6 +121,8 @@ describe('BioEntityDrawer - component', () => {
...DRAWER_INITIAL_STATE, ...DRAWER_INITIAL_STATE,
bioEntityDrawerState: { bioEntityDrawerState: {
bioentityId: bioEntityContentFixture.bioEntity.id, bioentityId: bioEntityContentFixture.bioEntity.id,
drugs: {},
chemicals: {},
}, },
}, },
}); });
...@@ -148,6 +157,8 @@ describe('BioEntityDrawer - component', () => { ...@@ -148,6 +157,8 @@ describe('BioEntityDrawer - component', () => {
...DRAWER_INITIAL_STATE, ...DRAWER_INITIAL_STATE,
bioEntityDrawerState: { bioEntityDrawerState: {
bioentityId: bioEntityContentFixture.bioEntity.id, bioentityId: bioEntityContentFixture.bioEntity.id,
drugs: {},
chemicals: {},
}, },
}, },
}); });
...@@ -173,6 +184,8 @@ describe('BioEntityDrawer - component', () => { ...@@ -173,6 +184,8 @@ describe('BioEntityDrawer - component', () => {
...DRAWER_INITIAL_STATE, ...DRAWER_INITIAL_STATE,
bioEntityDrawerState: { bioEntityDrawerState: {
bioentityId: bioEntity.id, bioentityId: bioEntity.id,
drugs: {},
chemicals: {},
}, },
}, },
}); });
...@@ -194,6 +207,8 @@ describe('BioEntityDrawer - component', () => { ...@@ -194,6 +207,8 @@ describe('BioEntityDrawer - component', () => {
...DRAWER_INITIAL_STATE, ...DRAWER_INITIAL_STATE,
bioEntityDrawerState: { bioEntityDrawerState: {
bioentityId: bioEntityContentFixture.bioEntity.id, bioentityId: bioEntityContentFixture.bioEntity.id,
drugs: {},
chemicals: {},
}, },
}, },
models: { models: {
...@@ -204,5 +219,157 @@ describe('BioEntityDrawer - component', () => { ...@@ -204,5 +219,157 @@ describe('BioEntityDrawer - component', () => {
expect(screen.getByTestId('associated-submap')).toBeInTheDocument(); expect(screen.getByTestId('associated-submap')).toBeInTheDocument();
}); });
it('should display chemicals list header', () => {
renderComponent({
bioEntity: {
data: [
{
searchQueryElement: '',
loading: 'succeeded',
error: { name: '', message: '' },
data: [
{
...bioEntityContentFixture,
bioEntity: {
...bioEntityContentFixture.bioEntity,
fullName: null,
},
},
],
},
],
loading: 'succeeded',
error: { message: '', name: '' },
},
drawer: {
...DRAWER_INITIAL_STATE,
bioEntityDrawerState: {
bioentityId: bioEntityContentFixture.bioEntity.id,
drugs: {},
chemicals: {},
},
},
});
expect(screen.getByText('Drugs for target')).toBeInTheDocument();
});
it('should display drugs list header', () => {
renderComponent({
bioEntity: {
data: [
{
searchQueryElement: '',
loading: 'succeeded',
error: { name: '', message: '' },
data: [
{
...bioEntityContentFixture,
bioEntity: {
...bioEntityContentFixture.bioEntity,
fullName: null,
},
},
],
},
],
loading: 'succeeded',
error: { message: '', name: '' },
},
drawer: {
...DRAWER_INITIAL_STATE,
bioEntityDrawerState: {
bioentityId: bioEntityContentFixture.bioEntity.id,
drugs: {},
chemicals: {},
},
},
});
expect(screen.getByText('Chemicals for target', { exact: false })).toBeInTheDocument();
});
it('should fetch drugs on drugs for target click', () => {
const { store } = renderComponent({
bioEntity: {
data: [
{
searchQueryElement: '',
loading: 'succeeded',
error: { name: '', message: '' },
data: [
{
...bioEntityContentFixture,
bioEntity: {
...bioEntityContentFixture.bioEntity,
fullName: null,
},
},
],
},
],
loading: 'succeeded',
error: { message: '', name: '' },
},
drawer: {
...DRAWER_INITIAL_STATE,
bioEntityDrawerState: {
bioentityId: bioEntityContentFixture.bioEntity.id,
drugs: {},
chemicals: {},
},
},
});
const button = screen.getByText('Drugs for target', { exact: false });
act(() => {
button.click();
});
expect(store.getActions()[0].type).toBe('drawer/getDrugsForBioEntityDrawerTarget/pending');
});
it('should fetch chemicals on chemicals for target click', () => {
const { store } = renderComponent({
bioEntity: {
data: [
{
searchQueryElement: '',
loading: 'succeeded',
error: { name: '', message: '' },
data: [
{
...bioEntityContentFixture,
bioEntity: {
...bioEntityContentFixture.bioEntity,
fullName: null,
},
},
],
},
],
loading: 'succeeded',
error: { message: '', name: '' },
},
drawer: {
...DRAWER_INITIAL_STATE,
bioEntityDrawerState: {
bioentityId: bioEntityContentFixture.bioEntity.id,
drugs: {},
chemicals: {},
},
},
});
const button = screen.getByText('Chemicals for target', { exact: false });
act(() => {
button.click();
});
expect(store.getActions()[0].type).toBe(
'drawer/getChemicalsForBioEntityDrawerTarget/pending',
);
});
}); });
}); });
import { ZERO } from '@/constants/common'; import { ZERO } from '@/constants/common';
import { searchedFromMapBioEntityElement } from '@/redux/bioEntity/bioEntity.selectors'; import {
searchedFromMapBioEntityElement,
searchedFromMapBioEntityElementRelatedSubmapSelector,
} from '@/redux/bioEntity/bioEntity.selectors';
import {
getChemicalsForBioEntityDrawerTarget,
getDrugsForBioEntityDrawerTarget,
} from '@/redux/drawer/drawer.thunks';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { useAppSelector } from '@/redux/hooks/useAppSelector'; import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { DrawerHeading } from '@/shared/DrawerHeading'; import { DrawerHeading } from '@/shared/DrawerHeading';
import { CollapsibleSection } from '../ExportDrawer/CollapsibleSection';
import { AnnotationItem } from './AnnotationItem'; import { AnnotationItem } from './AnnotationItem';
import { AssociatedSubmap } from './AssociatedSubmap'; import { AssociatedSubmap } from './AssociatedSubmap';
import { ChemicalsList } from './ChemicalsList';
import { DrugsList } from './DrugsList';
import { OverlayData } from './OverlayData'; import { OverlayData } from './OverlayData';
const TARGET_PREFIX: ElementSearchResultType = `ALIAS`;
export const BioEntityDrawer = (): React.ReactNode => { export const BioEntityDrawer = (): React.ReactNode => {
const dispatch = useAppDispatch();
const bioEntityData = useAppSelector(searchedFromMapBioEntityElement); const bioEntityData = useAppSelector(searchedFromMapBioEntityElement);
const relatedSubmap = useAppSelector(searchedFromMapBioEntityElementRelatedSubmapSelector);
const currentTargetId = bioEntityData?.id ? `${TARGET_PREFIX}:${bioEntityData.id}` : '';
const fetchChemicalsForTarget = (): void => {
dispatch(getChemicalsForBioEntityDrawerTarget(currentTargetId));
};
const fetchDrugsForTarget = (): void => {
dispatch(getDrugsForBioEntityDrawerTarget(currentTargetId));
};
if (!bioEntityData) { if (!bioEntityData) {
return null; return null;
...@@ -48,6 +71,16 @@ export const BioEntityDrawer = (): React.ReactNode => { ...@@ -48,6 +71,16 @@ export const BioEntityDrawer = (): React.ReactNode => {
/> />
))} ))}
<AssociatedSubmap /> <AssociatedSubmap />
{!relatedSubmap && (
<>
<CollapsibleSection title="Drugs for target" onOpened={fetchDrugsForTarget}>
<DrugsList />
</CollapsibleSection>
<CollapsibleSection title="Chemicals for target" onOpened={fetchChemicalsForTarget}>
<ChemicalsList />
</CollapsibleSection>
</>
)}
<OverlayData /> <OverlayData />
</div> </div>
</div> </div>
......
import { DEFAULT_FETCH_DATA } from '@/constants/fetchData';
import { chemicalsFixture } from '@/models/fixtures/chemicalsFixture';
import { INITIAL_STORE_STATE_MOCK } from '@/redux/root/root.fixtures';
import { StoreType } from '@/redux/store';
import { InitialStoreState } from '@/utils/testing/getReduxStoreActionsListener';
import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
import { render, screen } from '@testing-library/react';
import { ChemicalsList } from './ChemicalsList.component';
const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => {
const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState);
return (
render(
<Wrapper>
<ChemicalsList />
</Wrapper>,
),
{
store,
}
);
};
describe('ChemicalsList - component', () => {
describe('when chemicals data is loading', () => {
beforeEach(() => {
const bioEntityId = 5000;
renderComponent({
drawer: {
...INITIAL_STORE_STATE_MOCK.drawer,
bioEntityDrawerState: {
...INITIAL_STORE_STATE_MOCK.drawer,
bioentityId: bioEntityId,
drugs: {},
chemicals: {
[`${bioEntityId}`]: {
...DEFAULT_FETCH_DATA,
loading: 'pending',
data: [],
},
},
},
},
});
});
it('should show loading indicator', () => {
expect(screen.getByAltText('spinner icon')).toBeInTheDocument();
});
});
describe('when chemicals data is empty', () => {
beforeEach(() => {
const bioEntityId = 5000;
renderComponent({
drawer: {
...INITIAL_STORE_STATE_MOCK.drawer,
bioEntityDrawerState: {
...INITIAL_STORE_STATE_MOCK.drawer,
bioentityId: bioEntityId,
drugs: {},
chemicals: {
[`${bioEntityId}`]: {
...DEFAULT_FETCH_DATA,
loading: 'succeeded',
data: [],
},
},
},
},
});
});
it('should show text with info', () => {
expect(screen.getByText('List is empty')).toBeInTheDocument();
});
});
describe('when chemicals data is present and valid', () => {
beforeEach(() => {
const bioEntityId = 5000;
renderComponent({
drawer: {
...INITIAL_STORE_STATE_MOCK.drawer,
bioEntityDrawerState: {
...INITIAL_STORE_STATE_MOCK.drawer,
bioentityId: bioEntityId,
drugs: {},
chemicals: {
[`${bioEntityId}`]: {
...DEFAULT_FETCH_DATA,
loading: 'succeeded',
data: chemicalsFixture,
},
},
},
},
});
});
it.each(chemicalsFixture)('should show bio entitity card', chemical => {
expect(screen.getByText(chemical.name)).toBeInTheDocument();
});
});
describe('when chemicals data is present but for different bio entity id', () => {
beforeEach(() => {
const bioEntityId = 5000;
renderComponent({
drawer: {
...INITIAL_STORE_STATE_MOCK.drawer,
bioEntityDrawerState: {
...INITIAL_STORE_STATE_MOCK.drawer,
bioentityId: bioEntityId,
drugs: {},
chemicals: {
'2137': {
...DEFAULT_FETCH_DATA,
loading: 'succeeded',
data: chemicalsFixture,
},
},
},
},
});
});
it.each(chemicalsFixture)('should not show bio entitity card', chemical => {
expect(screen.queryByText(chemical.name)).not.toBeInTheDocument();
});
it('should show text with info', () => {
expect(screen.getByText('List is empty')).toBeInTheDocument();
});
});
});
import { currentSearchedBioEntityChemicalsSelector } from '@/redux/drawer/drawer.selectors';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { LoadingIndicator } from '@/shared/LoadingIndicator';
import { ZERO } from '@/constants/common';
import { BioEntitiesPinsListItem } from '../../SearchDrawerWrapper/BioEntitiesResultsList/BioEntitiesPinsList/BioEntitiesPinsListItem';
export const ChemicalsList = (): JSX.Element => {
const chemicals = useAppSelector(currentSearchedBioEntityChemicalsSelector);
const chemicalsData = chemicals.data || [];
if (chemicals.loading === 'pending') {
return <LoadingIndicator />;
}
return (
<div>
{chemicalsData.map(drug => (
<BioEntitiesPinsListItem key={`${drug.id}`} pin={drug} name={drug.name} />
))}
{chemicalsData.length === ZERO && 'List is empty'}
</div>
);
};
export { ChemicalsList } from './ChemicalsList.component';
import { DEFAULT_FETCH_DATA } from '@/constants/fetchData';
import { drugsFixture } from '@/models/fixtures/drugFixtures';
import { INITIAL_STORE_STATE_MOCK } from '@/redux/root/root.fixtures';
import { StoreType } from '@/redux/store';
import { InitialStoreState } from '@/utils/testing/getReduxStoreActionsListener';
import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore';
import { render, screen } from '@testing-library/react';
import { DrugsList } from './DrugsList.component';
const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => {
const { Wrapper, store } = getReduxWrapperWithStore(initialStoreState);
return (
render(
<Wrapper>
<DrugsList />
</Wrapper>,
),
{
store,
}
);
};
describe('DrugsList - component', () => {
describe('when drugs data is loading', () => {
beforeEach(() => {
const bioEntityId = 5000;
renderComponent({
drawer: {
...INITIAL_STORE_STATE_MOCK.drawer,
bioEntityDrawerState: {
...INITIAL_STORE_STATE_MOCK.drawer,
bioentityId: bioEntityId,
drugs: {
[`${bioEntityId}`]: {
...DEFAULT_FETCH_DATA,
loading: 'pending',
data: [],
},
},
chemicals: {},
},
},
});
});
it('should show loading indicator', () => {
expect(screen.getByAltText('spinner icon')).toBeInTheDocument();
});
});
describe('when drugs data is empty', () => {
beforeEach(() => {
const bioEntityId = 5000;
renderComponent({
drawer: {
...INITIAL_STORE_STATE_MOCK.drawer,
bioEntityDrawerState: {
...INITIAL_STORE_STATE_MOCK.drawer,
bioentityId: bioEntityId,
drugs: {
[`${bioEntityId}`]: {
...DEFAULT_FETCH_DATA,
loading: 'succeeded',
data: [],
},
},
chemicals: {},
},
},
});
});
it('should show text with info', () => {
expect(screen.getByText('List is empty')).toBeInTheDocument();
});
});
describe('when drugs data is present and valid', () => {
beforeEach(() => {
const bioEntityId = 5000;
renderComponent({
drawer: {
...INITIAL_STORE_STATE_MOCK.drawer,
bioEntityDrawerState: {
...INITIAL_STORE_STATE_MOCK.drawer,
bioentityId: bioEntityId,
drugs: {
[`${bioEntityId}`]: {
...DEFAULT_FETCH_DATA,
loading: 'succeeded',
data: drugsFixture,
},
},
chemicals: {},
},
},
});
});
it.each(drugsFixture)('should show bio entitity card', drug => {
expect(screen.getByText(drug.name)).toBeInTheDocument();
});
});
describe('when drugs data is present but for different bio entity id', () => {
beforeEach(() => {
const bioEntityId = 5000;
renderComponent({
drawer: {
...INITIAL_STORE_STATE_MOCK.drawer,
bioEntityDrawerState: {
...INITIAL_STORE_STATE_MOCK.drawer,
bioentityId: bioEntityId,
drugs: {
'2137': {
...DEFAULT_FETCH_DATA,
loading: 'succeeded',
data: drugsFixture,
},
},
chemicals: {},
},
},
});
});
it.each(drugsFixture)('should not show bio entitity card', drug => {
expect(screen.queryByText(drug.name)).not.toBeInTheDocument();
});
it('should show text with info', () => {
expect(screen.getByText('List is empty')).toBeInTheDocument();
});
});
});
import { ZERO } from '@/constants/common';
import { currentSearchedBioEntityDrugsSelector } from '@/redux/drawer/drawer.selectors';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { LoadingIndicator } from '@/shared/LoadingIndicator';
import { BioEntitiesPinsListItem } from '../../SearchDrawerWrapper/BioEntitiesResultsList/BioEntitiesPinsList/BioEntitiesPinsListItem';
export const DrugsList = (): JSX.Element => {
const drugs = useAppSelector(currentSearchedBioEntityDrugsSelector);
const drugsData = drugs.data || [];
if (drugs.loading === 'pending') {
return <LoadingIndicator />;
}
return (
<div>
{drugsData.map(drug => (
<BioEntitiesPinsListItem key={`${drug.id}`} pin={drug} name={drug.name} />
))}
{drugsData.length === ZERO && 'List is empty'}
</div>
);
};
export { DrugsList } from './DrugsList.component';
import { ZERO } from '@/constants/common';
import { import {
Accordion, Accordion,
AccordionItem, AccordionItem,
...@@ -5,22 +6,35 @@ import { ...@@ -5,22 +6,35 @@ import {
AccordionItemHeading, AccordionItemHeading,
AccordionItemPanel, AccordionItemPanel,
} from '@/shared/Accordion'; } from '@/shared/Accordion';
import { ID } from 'react-accessible-accordion/dist/types/components/ItemContext';
type CollapsibleSectionProps = { type CollapsibleSectionProps = {
title: string; title: string;
children: React.ReactNode; children: React.ReactNode;
onOpened?(): void;
}; };
export const CollapsibleSection = ({ export const CollapsibleSection = ({
title, title,
children, children,
}: CollapsibleSectionProps): React.ReactNode => ( onOpened,
<Accordion allowZeroExpanded> }: CollapsibleSectionProps): React.ReactNode => {
<AccordionItem> const handleOnChange = (ids: ID[]): void => {
<AccordionItemHeading> const hasBeenOpened = ids.length > ZERO;
<AccordionItemButton>{title}</AccordionItemButton>
</AccordionItemHeading> if (hasBeenOpened && onOpened) {
<AccordionItemPanel>{children}</AccordionItemPanel> onOpened();
</AccordionItem> }
</Accordion> };
);
return (
<Accordion allowZeroExpanded onChange={handleOnChange}>
<AccordionItem>
<AccordionItemHeading>
<AccordionItemButton>{title}</AccordionItemButton>
</AccordionItemHeading>
<AccordionItemPanel>{children}</AccordionItemPanel>
</AccordionItem>
</Accordion>
);
};
/* eslint-disable no-magic-numbers */ /* eslint-disable no-magic-numbers */
import { render, screen } from '@testing-library/react'; import { bioEntitiesContentFixture } from '@/models/fixtures/bioEntityContentsFixture';
import { StoreType } from '@/redux/store';
import { BioEntityContent } from '@/types/models';
import { import {
InitialStoreState, InitialStoreState,
getReduxWrapperWithStore, getReduxWrapperWithStore,
} from '@/utils/testing/getReduxWrapperWithStore'; } from '@/utils/testing/getReduxWrapperWithStore';
import { bioEntitiesContentFixture } from '@/models/fixtures/bioEntityContentsFixture'; import { render, screen } from '@testing-library/react';
import { StoreType } from '@/redux/store';
import { BioEntityContent } from '@/types/models';
import { BioEntitiesPinsList } from './BioEntitiesPinsList.component'; import { BioEntitiesPinsList } from './BioEntitiesPinsList.component';
const renderComponent = ( const renderComponent = (
...@@ -30,7 +30,10 @@ const renderComponent = ( ...@@ -30,7 +30,10 @@ const renderComponent = (
describe('BioEntitiesPinsList - component ', () => { describe('BioEntitiesPinsList - component ', () => {
it('should display list of bio entites elements', () => { it('should display list of bio entites elements', () => {
renderComponent(bioEntitiesContentFixture); renderComponent(bioEntitiesContentFixture);
const bioEntitiesWithFullName = bioEntitiesContentFixture.filter(({ bioEntity }) =>
Boolean(bioEntity.fullName),
);
expect(screen.getAllByTestId('bio-entity-name')).toHaveLength(10); expect(screen.getAllByTestId('bio-entity-name')).toHaveLength(bioEntitiesWithFullName.length);
}); });
}); });
/* eslint-disable no-magic-numbers */ /* eslint-disable no-magic-numbers */
import { render, screen } from '@testing-library/react'; import { bioEntitiesContentFixture } from '@/models/fixtures/bioEntityContentsFixture';
import { StoreType } from '@/redux/store';
import { BioEntity } from '@/types/models';
import { import {
InitialStoreState, InitialStoreState,
getReduxWrapperWithStore, getReduxWrapperWithStore,
} from '@/utils/testing/getReduxWrapperWithStore'; } from '@/utils/testing/getReduxWrapperWithStore';
import { bioEntitiesContentFixture } from '@/models/fixtures/bioEntityContentsFixture'; import { render, screen } from '@testing-library/react';
import { StoreType } from '@/redux/store';
import { BioEntity } from '@/types/models';
import { BioEntitiesPinsListItem } from './BioEntitiesPinsListItem.component'; import { BioEntitiesPinsListItem } from './BioEntitiesPinsListItem.component';
const BIO_ENTITY = bioEntitiesContentFixture[0].bioEntity; const BIO_ENTITY = bioEntitiesContentFixture[0].bioEntity;
...@@ -53,7 +53,7 @@ describe('BioEntitiesPinsListItem - component ', () => { ...@@ -53,7 +53,7 @@ describe('BioEntitiesPinsListItem - component ', () => {
renderComponent(bioEntity.name, bioEntity); renderComponent(bioEntity.name, bioEntity);
expect(screen.getAllByTestId('bio-entity-symbol')[0].textContent).toHaveLength(0); expect(screen.queryAllByTestId('bio-entity-symbol')).toHaveLength(0);
}); });
it('should display string type of bio entity element', () => { it('should display string type of bio entity element', () => {
renderComponent(BIO_ENTITY.name, BIO_ENTITY); renderComponent(BIO_ENTITY.name, BIO_ENTITY);
......
import { twMerge } from 'tailwind-merge';
import { Icon } from '@/shared/Icon'; import { Icon } from '@/shared/Icon';
import { BioEntity } from '@/types/models'; import { twMerge } from 'tailwind-merge';
import { getPinColor } from '../../../ResultsList/PinsList/PinsListItem/PinsListItem.component.utils'; import { getPinColor } from '../../../ResultsList/PinsList/PinsListItem/PinsListItem.component.utils';
import { PinListBioEntity } from './BioEntitiesPinsListItem.types';
interface BioEntitiesPinsListItemProps { interface BioEntitiesPinsListItemProps {
name: string; name: string;
pin: BioEntity; pin: PinListBioEntity;
} }
export const BioEntitiesPinsListItem = ({ export const BioEntitiesPinsListItem = ({
...@@ -17,21 +17,26 @@ export const BioEntitiesPinsListItem = ({ ...@@ -17,21 +17,26 @@ export const BioEntitiesPinsListItem = ({
<div className="flex w-full flex-row items-center gap-2"> <div className="flex w-full flex-row items-center gap-2">
<Icon name="pin" className={twMerge('mr-2 shrink-0', getPinColor('bioEntity'))} /> <Icon name="pin" className={twMerge('mr-2 shrink-0', getPinColor('bioEntity'))} />
<p> <p>
{pin.stringType}: <span className="w-full font-bold">{name}</span> {pin.stringType ? `${pin.stringType}: ` : ''}
<span className="w-full font-bold">{name}</span>
</p> </p>
</div> </div>
<p className="font-bold leading-6"> {pin.fullName && (
Full name:{' '} <p className="font-bold leading-6">
<span className="w-full font-normal" data-testid="bio-entity-name"> Full name:{' '}
{pin.fullName || ``} <span className="w-full font-normal" data-testid="bio-entity-name">
</span> {pin.fullName}
</p> </span>
<p className="font-bold leading-6"> </p>
Symbol:{' '} )}
<span className="w-full font-normal" data-testid="bio-entity-symbol"> {pin.symbol && (
{pin.symbol || ``} <p className="font-bold leading-6">
</span> Symbol:{' '}
</p> <span className="w-full font-normal" data-testid="bio-entity-symbol">
{pin.symbol}
</span>
</p>
)}
<p className="font-bold leading-6"> <p className="font-bold leading-6">
Synonyms: <span className="w-full font-normal">{pin.synonyms.join(', ')}</span> Synonyms: <span className="w-full font-normal">{pin.synonyms.join(', ')}</span>
</p> </p>
......
import { BioEntity } from '@/types/models';
export type PinListBioEntity = Pick<BioEntity, 'synonyms' | 'references'> & {
symbol?: BioEntity['symbol'];
stringType?: BioEntity['stringType'];
fullName?: BioEntity['fullName'];
};
import { FetchDataState } from '@/types/fetchDataState';
import { DEFAULT_ERROR } from './errors';
export const DEFAULT_FETCH_DATA: FetchDataState<[]> = {
data: [],
loading: 'idle',
error: DEFAULT_ERROR,
};
import { z } from 'zod';
export const targetSearchNameResult = z.object({
name: z.string(),
});
...@@ -36,6 +36,10 @@ export const apiPath = { ...@@ -36,6 +36,10 @@ export const apiPath = {
`projects/${PROJECT_ID}/models/*/bioEntities/reactions/?id=${ids.join(',')}&size=1000`, `projects/${PROJECT_ID}/models/*/bioEntities/reactions/?id=${ids.join(',')}&size=1000`,
getDrugsStringWithQuery: (searchQuery: string): string => getDrugsStringWithQuery: (searchQuery: string): string =>
`projects/${PROJECT_ID}/drugs:search?query=${searchQuery}`, `projects/${PROJECT_ID}/drugs:search?query=${searchQuery}`,
getDrugsStringWithColumnsTarget: (columns: string, target: string): string =>
`projects/${PROJECT_ID}/drugs:search?columns=${columns}&target=${target}`,
getChemicalsStringWithColumnsTarget: (columns: string, target: string): string =>
`projects/${PROJECT_ID}/chemicals:search?columns=${columns}&target=${target}`,
getModelsString: (): string => `projects/${PROJECT_ID}/models/`, getModelsString: (): string => `projects/${PROJECT_ID}/models/`,
getChemicalsStringWithQuery: (searchQuery: string): string => getChemicalsStringWithQuery: (searchQuery: string): string =>
`projects/${PROJECT_ID}/chemicals:search?query=${searchQuery}`, `projects/${PROJECT_ID}/chemicals:search?query=${searchQuery}`,
......
...@@ -11,7 +11,10 @@ export const DRAWER_INITIAL_STATE: DrawerState = { ...@@ -11,7 +11,10 @@ export const DRAWER_INITIAL_STATE: DrawerState = {
selectedSearchElement: '', selectedSearchElement: '',
}, },
reactionDrawerState: {}, reactionDrawerState: {},
bioEntityDrawerState: {}, bioEntityDrawerState: {
drugs: {},
chemicals: {},
},
overlayDrawerState: { overlayDrawerState: {
currentStep: 0, currentStep: 0,
}, },
......
...@@ -6,7 +6,11 @@ import type { ...@@ -6,7 +6,11 @@ import type {
OpenSearchDrawerWithSelectedTabReducerAction, OpenSearchDrawerWithSelectedTabReducerAction,
} from '@/redux/drawer/drawer.types'; } from '@/redux/drawer/drawer.types';
import type { DrawerName } from '@/types/drawerName'; import type { DrawerName } from '@/types/drawerName';
import type { PayloadAction } from '@reduxjs/toolkit'; import type { ActionReducerMapBuilder, PayloadAction } from '@reduxjs/toolkit';
import {
getChemicalsForBioEntityDrawerTarget,
getDrugsForBioEntityDrawerTarget,
} from './drawer.thunks';
export const openDrawerReducer = (state: DrawerState, action: PayloadAction<DrawerName>): void => { export const openDrawerReducer = (state: DrawerState, action: PayloadAction<DrawerName>): void => {
state.isOpen = true; state.isOpen = true;
...@@ -109,3 +113,59 @@ export const openBioEntityDrawerByIdReducer = ( ...@@ -109,3 +113,59 @@ export const openBioEntityDrawerByIdReducer = (
state.bioEntityDrawerState.bioentityId = action.payload; state.bioEntityDrawerState.bioentityId = action.payload;
state.searchDrawerState.selectedSearchElement = action.payload.toString(); state.searchDrawerState.selectedSearchElement = action.payload.toString();
}; };
export const getBioEntityDrugsForTargetReducers = (
builder: ActionReducerMapBuilder<DrawerState>,
): void => {
builder.addCase(getDrugsForBioEntityDrawerTarget.pending, state => {
const bioEntityId = state.bioEntityDrawerState.bioentityId || '';
state.bioEntityDrawerState.drugs[bioEntityId] = {
...state.bioEntityDrawerState.drugs[bioEntityId],
loading: 'pending',
};
});
builder.addCase(getDrugsForBioEntityDrawerTarget.fulfilled, (state, action) => {
const bioEntityId = state.bioEntityDrawerState.bioentityId || '';
state.bioEntityDrawerState.drugs[bioEntityId] = {
...state.bioEntityDrawerState.drugs[bioEntityId],
data: action.payload || [],
loading: 'succeeded',
};
});
builder.addCase(getDrugsForBioEntityDrawerTarget.rejected, state => {
const bioEntityId = state.bioEntityDrawerState.bioentityId || '';
state.bioEntityDrawerState.drugs[bioEntityId] = {
...state.bioEntityDrawerState.drugs[bioEntityId],
loading: 'failed',
};
// TODO to discuss manage state of failure
});
};
export const getBioEntityChemicalsForTargetReducers = (
builder: ActionReducerMapBuilder<DrawerState>,
): void => {
builder.addCase(getChemicalsForBioEntityDrawerTarget.pending, state => {
const bioEntityId = state.bioEntityDrawerState.bioentityId || '';
state.bioEntityDrawerState.chemicals[bioEntityId] = {
...state.bioEntityDrawerState.chemicals[bioEntityId],
loading: 'pending',
};
});
builder.addCase(getChemicalsForBioEntityDrawerTarget.fulfilled, (state, action) => {
const bioEntityId = state.bioEntityDrawerState.bioentityId || '';
state.bioEntityDrawerState.chemicals[bioEntityId] = {
...state.bioEntityDrawerState.chemicals[bioEntityId],
data: action.payload || [],
loading: 'succeeded',
};
});
builder.addCase(getChemicalsForBioEntityDrawerTarget.rejected, state => {
const bioEntityId = state.bioEntityDrawerState.bioentityId || '';
state.bioEntityDrawerState.chemicals[bioEntityId] = {
...state.bioEntityDrawerState.chemicals[bioEntityId],
loading: 'failed',
};
// TODO to discuss manage state of failure
});
};
import { DEFAULT_FETCH_DATA } from '@/constants/fetchData';
import { rootSelector } from '@/redux/root/root.selectors'; import { rootSelector } from '@/redux/root/root.selectors';
import { assertNever } from '@/utils/assertNever'; import { assertNever } from '@/utils/assertNever';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
...@@ -46,6 +47,24 @@ export const currentSearchedBioEntityId = createSelector( ...@@ -46,6 +47,24 @@ export const currentSearchedBioEntityId = createSelector(
state => state.bioentityId, state => state.bioentityId,
); );
export const currentSearchedBioEntityDrugsSelector = createSelector(
bioEntityDrawerStateSelector,
currentSearchedBioEntityId,
(state, currentBioEntityId) =>
currentBioEntityId && state.drugs[currentBioEntityId]
? state.drugs[currentBioEntityId]
: DEFAULT_FETCH_DATA,
);
export const currentSearchedBioEntityChemicalsSelector = createSelector(
bioEntityDrawerStateSelector,
currentSearchedBioEntityId,
(state, currentBioEntityId) =>
currentBioEntityId && state.chemicals[currentBioEntityId]
? state.chemicals[currentBioEntityId]
: DEFAULT_FETCH_DATA,
);
export const resultListSelector = createSelector( export const resultListSelector = createSelector(
rootSelector, rootSelector,
currentStepTypeSelector, currentStepTypeSelector,
......
import { createSlice } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit';
import { DRAWER_INITIAL_STATE } from './drawer.constants';
import { import {
closeDrawerReducer, closeDrawerReducer,
displayAddOverlaysDrawerReducer,
displayBioEntitiesListReducer, displayBioEntitiesListReducer,
displayChemicalsListReducer, displayChemicalsListReducer,
displayDrugsListReducer, displayDrugsListReducer,
displayEntityDetailsReducer, displayEntityDetailsReducer,
displayGroupedSearchResultsReducer, displayGroupedSearchResultsReducer,
getBioEntityChemicalsForTargetReducers,
getBioEntityDrugsForTargetReducers,
openBioEntityDrawerByIdReducer,
openDrawerReducer, openDrawerReducer,
openOverlaysDrawerReducer, openOverlaysDrawerReducer,
openBioEntityDrawerByIdReducer,
openReactionDrawerByIdReducer, openReactionDrawerByIdReducer,
openSearchDrawerWithSelectedTabReducer, openSearchDrawerWithSelectedTabReducer,
openSubmapsDrawerReducer, openSubmapsDrawerReducer,
selectTabReducer, selectTabReducer,
displayAddOverlaysDrawerReducer,
} from './drawer.reducers'; } from './drawer.reducers';
import { DRAWER_INITIAL_STATE } from './drawer.constants';
const drawerSlice = createSlice({ const drawerSlice = createSlice({
name: 'drawer', name: 'drawer',
...@@ -36,6 +38,10 @@ const drawerSlice = createSlice({ ...@@ -36,6 +38,10 @@ const drawerSlice = createSlice({
openReactionDrawerById: openReactionDrawerByIdReducer, openReactionDrawerById: openReactionDrawerByIdReducer,
openBioEntityDrawerById: openBioEntityDrawerByIdReducer, openBioEntityDrawerById: openBioEntityDrawerByIdReducer,
}, },
extraReducers: builder => {
getBioEntityDrugsForTargetReducers(builder);
getBioEntityChemicalsForTargetReducers(builder);
},
}); });
export const { export const {
......
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