Skip to content
Snippets Groups Projects
Commit 4a209e78 authored by Adrian Orłów's avatar Adrian Orłów :fire:
Browse files

Merge branch 'MIN-210-multisearch-should-display-all-pins' into 'development'

feat: add multisearch (MIN-210)

Closes MIN-210

See merge request !163
parents 197fb056 06d92953
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...,!163feat: add multisearch (MIN-210)
Pipeline #88173 passed
Showing
with 532 additions and 30 deletions
import lensIcon from '@/assets/vectors/icons/lens.svg';
import { searchDrawerOpenSelector } from '@/redux/drawer/drawer.selectors';
import {
currentSelectedSearchElement,
searchDrawerOpenSelector,
} from '@/redux/drawer/drawer.selectors';
import { openSearchDrawerWithSelectedTab, selectTab } from '@/redux/drawer/drawer.slice';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { resetReactionsData } from '@/redux/reactions/reactions.slice';
......@@ -21,6 +24,7 @@ export const SearchBar = (): JSX.Element => {
const isPendingSearchStatus = useSelector(isPendingSearchStatusSelector);
const isSearchDrawerOpen = useSelector(searchDrawerOpenSelector);
const isPerfectMatch = useSelector(perfectMatchSelector);
const currentTab = useSelector(currentSelectedSearchElement);
const dispatch = useAppDispatch();
const router = useRouter();
......@@ -61,6 +65,12 @@ export const SearchBar = (): JSX.Element => {
}
};
const handleSearchClick = (): void => {
if (!currentTab) return;
openSearchDrawerIfClosed(currentTab);
};
useEffect(() => {
updateSearchValueFromQueryParam();
}, [updateSearchValueFromQueryParam]);
......@@ -75,6 +85,7 @@ export const SearchBar = (): JSX.Element => {
onKeyDown={handleKeyPress}
onChange={onSearchChange}
disabled={isPendingSearchStatus}
onClick={handleSearchClick}
className="h-9 w-72 rounded-[64px] border border-transparent bg-cultured px-4 py-2.5 text-xs font-medium text-font-400 outline-none hover:border-greyscale-600 focus:border-greyscale-600"
/>
<button
......
import { act, render, screen } from '@testing-library/react';
import { StoreType } from '@/redux/store';
import {
InitialStoreState,
getReduxWrapperWithStore,
} from '@/utils/testing/getReduxWrapperWithStore';
import { act, render, screen } from '@testing-library/react';
import { SearchDrawerTabs } from './SearchDrawerTabs.component';
const renderComponent = (initialStoreState: InitialStoreState = {}): { store: StoreType } => {
......@@ -56,4 +56,30 @@ describe('SearchDrawerTabs - component', () => {
expect(selectedSearchElement).toBe('nadh');
});
it('should display no tabs when no search values', () => {
renderComponent({
search: {
searchValue: [],
loading: 'idle',
perfectMatch: false,
},
});
expect(screen.queryByText('aspirin')).toBeNull();
expect(screen.queryByText('nadh')).toBeNull();
});
it('should display no tabs when empty search value', () => {
renderComponent({
search: {
searchValue: [''],
loading: 'idle',
perfectMatch: false,
},
});
expect(screen.queryByText('aspirin')).toBeNull();
expect(screen.queryByText('nadh')).toBeNull();
});
});
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { searchValueSelector } from '@/redux/search/search.selectors';
import { FIRST_ARRAY_ELEMENT, SIZE_OF_EMPTY_ARRAY } from '@/constants/common';
import { currentSelectedSearchElement } from '@/redux/drawer/drawer.selectors';
import { selectTab } from '@/redux/drawer/drawer.slice';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { searchValueSelector } from '@/redux/search/search.selectors';
import { twMerge } from 'tailwind-merge';
export const SearchDrawerTabs = (): JSX.Element => {
export const SearchDrawerTabs = (): JSX.Element | null => {
const dispatch = useAppDispatch();
const searchValue = useAppSelector(searchValueSelector);
const selectedSearchValue = useAppSelector(currentSelectedSearchElement);
......@@ -16,6 +17,10 @@ export const SearchDrawerTabs = (): JSX.Element => {
dispatch(selectTab(value));
};
if (searchValue.length === SIZE_OF_EMPTY_ARRAY || searchValue[FIRST_ARRAY_ELEMENT] === '') {
return null;
}
return (
<div className="flex items-center justify-between border-b border-b-divide px-6">
<div className="text-center text-sm">
......
import { PIN_PATH2D, PIN_SIZE } from '@/constants/canvas';
import { PIN_PATH2D, PIN_SIZE, TEXT_COLOR } from '@/constants/canvas';
import { HALF, ONE_AND_HALF, QUARTER, THIRD, TWO_AND_HALF } from '@/constants/dividers';
import { DEFAULT_FONT_FAMILY } from '@/constants/font';
import { Point } from '@/types/map';
......@@ -12,6 +12,7 @@ const BIG_TEXT_VALUE = 100;
interface Args {
color: string;
value: number;
textColor?: string;
}
export const drawPinOnCanvas = (
......@@ -42,7 +43,7 @@ export const getTextPosition = (textWidth: number, textHeight: number): Point =>
});
export const drawNumberOnCanvas = (
{ value }: Pick<Args, 'value'>,
{ value, textColor }: Pick<Args, 'value' | 'textColor'>,
ctx: CanvasRenderingContext2D,
): void => {
const text = `${value}`;
......@@ -53,7 +54,7 @@ export const drawNumberOnCanvas = (
const textHeight = textMetrics.fontBoundingBoxAscent + textMetrics.fontBoundingBoxDescent;
const { x, y } = getTextPosition(textWidth, textHeight);
ctx.fillStyle = 'white';
ctx.fillStyle = textColor || TEXT_COLOR;
ctx.textBaseline = 'top';
ctx.font = `${fontSize}px ${DEFAULT_FONT_FAMILY}`;
ctx.fillText(text, x, y);
......@@ -70,7 +71,7 @@ export const getCanvasIcon = (
drawPinOnCanvas(args, ctx);
if (args?.value !== undefined) {
drawNumberOnCanvas({ value: args.value }, ctx);
drawNumberOnCanvas({ value: args.value, textColor: args?.textColor }, ctx);
}
return canvas;
......
......@@ -11,10 +11,12 @@ export const getBioEntitiesFeatures = (
pointToProjection,
type,
entityNumber,
activeIds,
}: {
pointToProjection: UsePointToProjectionResult;
type: PinType;
entityNumber: EntityNumber;
activeIds: (string | number)[];
},
): Feature[] => {
return bioEntites.map(bioEntity =>
......@@ -23,6 +25,7 @@ export const getBioEntitiesFeatures = (
type,
// pin's index number
value: entityNumber?.[bioEntity.elementId],
isActive: activeIds.includes(bioEntity.id),
}),
);
};
import { FIRST_ARRAY_ELEMENT } from '@/constants/common';
import { bioEntitiesContentFixture } from '@/models/fixtures/bioEntityContentsFixture';
import { initialMapStateFixture } from '@/redux/map/map.fixtures';
import { PinType } from '@/types/pin';
......@@ -36,6 +37,7 @@ describe('getBioEntitiesFeatures - subUtil', () => {
pointToProjection,
type,
entityNumber: {},
activeIds: [bioEntities[FIRST_ARRAY_ELEMENT].id],
});
result.forEach(resultElement => {
......
import { PINS_COLORS } from '@/constants/canvas';
/* eslint-disable no-magic-numbers */
import { PINS_COLORS, TEXT_COLOR } from '@/constants/canvas';
import { bioEntityContentFixture } from '@/models/fixtures/bioEntityContentsFixture';
import { initialMapStateFixture } from '@/redux/map/map.fixtures';
import { PinType } from '@/types/pin';
import { addAlphaToHexString } from '@/utils/convert/addAlphaToHexString';
import { UsePointToProjectionResult, usePointToProjection } from '@/utils/map/usePointToProjection';
import {
GetReduxWrapperUsingSliceReducer,
......@@ -18,8 +20,6 @@ jest.mock('./getPinStyle', () => ({
...jest.requireActual('./getPinStyle'),
}));
const getPinStyleSpy = jest.spyOn(getPinStyle, 'getPinStyle');
const getPointToProjection = (
wrapper: ReturnType<GetReduxWrapperUsingSliceReducer>['Wrapper'],
): UsePointToProjectionResult => {
......@@ -45,6 +45,7 @@ describe('getBioEntitySingleFeature - subUtil', () => {
pointToProjection,
type,
value,
isActive: true,
});
const style = result.getStyle() as Style;
......@@ -53,16 +54,40 @@ describe('getBioEntitySingleFeature - subUtil', () => {
expect(style).toBeInstanceOf(Style);
});
it.each(pinTypes)('should run getPinStyle with valid args for type=%s', async type => {
it.each(pinTypes)('should run getPinStyle with valid args for type=%s', type => {
const getPinStyleSpy = jest.spyOn(getPinStyle, 'getPinStyle');
getBioEntitySingleFeature(bioEntity, {
pointToProjection,
type,
value,
isActive: true,
});
expect(getPinStyleSpy).toHaveBeenCalledWith({
color: PINS_COLORS[type],
value,
textColor: TEXT_COLOR,
});
});
it.each(pinTypes)(
'should run getPinStyle with valid args for isActive=fasle and type=%s',
type => {
const getPinStyleSpy = jest.spyOn(getPinStyle, 'getPinStyle');
getBioEntitySingleFeature(bioEntity, {
pointToProjection,
type,
value,
isActive: false,
});
expect(getPinStyleSpy).toHaveBeenCalledWith({
color: addAlphaToHexString(PINS_COLORS[type], 0.5),
value,
textColor: addAlphaToHexString(TEXT_COLOR, 0.5),
});
},
);
});
import { PINS_COLORS } from '@/constants/canvas';
import { PINS_COLORS, TEXT_COLOR } from '@/constants/canvas';
import { BioEntity } from '@/types/models';
import { PinType } from '@/types/pin';
import { addAlphaToHexString } from '@/utils/convert/addAlphaToHexString';
import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
import { Feature } from 'ol';
import { getPinFeature } from './getPinFeature';
import { getPinStyle } from './getPinStyle';
const INACTIVE_ELEMENT_OPACITY = 0.5;
export const getBioEntitySingleFeature = (
bioEntity: BioEntity,
{
pointToProjection,
type,
value,
isActive,
}: {
pointToProjection: UsePointToProjectionResult;
type: PinType;
value: number;
isActive: boolean;
},
): Feature => {
const color = isActive
? PINS_COLORS[type]
: addAlphaToHexString(PINS_COLORS[type], INACTIVE_ELEMENT_OPACITY);
const textColor = isActive
? TEXT_COLOR
: addAlphaToHexString(TEXT_COLOR, INACTIVE_ELEMENT_OPACITY);
const feature = getPinFeature(bioEntity, pointToProjection);
const style = getPinStyle({
color: PINS_COLORS[type],
color,
value,
textColor,
});
feature.setStyle(style);
......
......@@ -4,7 +4,15 @@ import Icon from 'ol/style/Icon';
import Style from 'ol/style/Style';
import { getCanvasIcon } from '../getCanvasIcon';
export const getPinStyle = ({ value, color }: { value?: number; color: string }): Style =>
export const getPinStyle = ({
value,
color,
textColor,
}: {
value?: number;
color: string;
textColor?: string;
}): Style =>
new Style({
image: new Icon({
displacement: [ZERO, PIN_SIZE.height],
......@@ -13,6 +21,7 @@ export const getPinStyle = ({ value, color }: { value?: number; color: string })
img: getCanvasIcon({
color,
value,
textColor,
}),
}),
});
/* eslint-disable no-magic-numbers */
import { searchedBioEntitesSelectorOfCurrentMap } from '@/redux/bioEntity/bioEntity.selectors';
import { searchedChemicalsBioEntitesOfCurrentMapSelector } from '@/redux/chemicals/chemicals.selectors';
import { searchedDrugsBioEntitesOfCurrentMapSelector } from '@/redux/drugs/drugs.selectors';
import {
allBioEntitesSelectorOfCurrentMap,
allVisibleBioEntitiesIdsSelector,
} from '@/redux/bioEntity/bioEntity.selectors';
import { allChemicalsBioEntitesOfCurrentMapSelector } from '@/redux/chemicals/chemicals.selectors';
import { allDrugsBioEntitesOfCurrentMapSelector } from '@/redux/drugs/drugs.selectors';
import { entityNumberDataSelector } from '@/redux/entityNumber/entityNumber.selectors';
import { markersPinsOfCurrentMapDataSelector } from '@/redux/markers/markers.selectors';
import { usePointToProjection } from '@/utils/map/usePointToProjection';
......@@ -16,9 +19,10 @@ import { getMarkersFeatures } from './getMarkersFeatures';
export const useOlMapPinsLayer = (): VectorLayer<VectorSource<Feature<Geometry>>> => {
const pointToProjection = usePointToProjection();
const contentBioEntites = useSelector(searchedBioEntitesSelectorOfCurrentMap);
const chemicalsBioEntities = useSelector(searchedChemicalsBioEntitesOfCurrentMapSelector);
const drugsBioEntities = useSelector(searchedDrugsBioEntitesOfCurrentMapSelector);
const activeIds = useSelector(allVisibleBioEntitiesIdsSelector);
const contentBioEntites = useSelector(allBioEntitesSelectorOfCurrentMap);
const chemicalsBioEntities = useSelector(allChemicalsBioEntitesOfCurrentMapSelector);
const drugsBioEntities = useSelector(allDrugsBioEntitesOfCurrentMapSelector);
const markersEntities = useSelector(markersPinsOfCurrentMapDataSelector);
const entityNumber = useSelector(entityNumberDataSelector);
......@@ -29,16 +33,19 @@ export const useOlMapPinsLayer = (): VectorLayer<VectorSource<Feature<Geometry>>
pointToProjection,
type: 'bioEntity',
entityNumber,
activeIds,
}),
getBioEntitiesFeatures(chemicalsBioEntities, {
pointToProjection,
type: 'chemicals',
entityNumber,
activeIds,
}),
getBioEntitiesFeatures(drugsBioEntities, {
pointToProjection,
type: 'drugs',
entityNumber,
activeIds,
}),
getMarkersFeatures(markersEntities, { pointToProjection }),
].flat(),
......@@ -49,6 +56,7 @@ export const useOlMapPinsLayer = (): VectorLayer<VectorSource<Feature<Geometry>>
pointToProjection,
markersEntities,
entityNumber,
activeIds,
],
);
......
......@@ -55,7 +55,6 @@ describe('handleFeaturesClick - util', () => {
const { dispatch: localDispatch } = localStore;
handleFeaturesClick(features, localDispatch);
expect(store.getActions()).toStrictEqual([
{ payload: undefined, type: 'search/clearSearchData' },
{ payload: 1234, type: 'drawer/openBioEntityDrawerById' },
]);
});
......
import { SIZE_OF_EMPTY_ARRAY } from '@/constants/common';
import { FEATURE_TYPE, PIN_ICON_ANY, SURFACE_ANY } from '@/constants/features';
import { openBioEntityDrawerById } from '@/redux/drawer/drawer.slice';
import { clearSearchData } from '@/redux/search/search.slice';
import { AppDispatch } from '@/redux/store';
import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus';
import { FeatureLike } from 'ol/Feature';
......@@ -23,7 +22,6 @@ export const handleFeaturesClick = (
PluginsEventBus.dispatchEvent('onPinIconClick', { id: pinId });
if (pin.get('type') === FEATURE_TYPE.PIN_ICON_BIOENTITY) {
dispatch(clearSearchData());
dispatch(openBioEntityDrawerById(pinId));
}
});
......
import { ZERO } from '@/constants/common';
import { bioEntityContentFixture } from '@/models/fixtures/bioEntityContentsFixture';
import { initialStateFixture } from '@/redux/drawer/drawerFixture';
import { INITIAL_STORE_STATE_MOCK } from '@/redux/root/root.fixtures';
import { getReduxStoreWithActionsListener } from '@/utils/testing/getReduxStoreActionsListener';
import { renderHook } from '@testing-library/react';
import { useHandlePinIconClick } from './useHandlePinIconClick';
describe('useHandlePinIconClick - util', () => {
describe('when pin clicked', () => {
describe('when tab is invalid', () => {
const pinId = 123;
const { Wrapper, store } = getReduxStoreWithActionsListener({
...INITIAL_STORE_STATE_MOCK,
bioEntity: {
data: [
{
searchQueryElement: '',
loading: 'succeeded',
error: { name: '', message: '' },
data: [
{
...bioEntityContentFixture,
bioEntity: {
...bioEntityContentFixture.bioEntity,
fullName: null,
id: pinId,
model: ZERO,
},
},
],
},
],
loading: 'succeeded',
error: { message: '', name: '' },
},
});
const {
result: { current: onPinIconClick },
} = renderHook(() => useHandlePinIconClick(), {
wrapper: Wrapper,
});
onPinIconClick({ id: pinId });
it('should do not dispatch any action', () => {
const actions = store.getActions();
expect(actions).toStrictEqual([]);
});
});
describe('when tab is already selected', () => {
const pinId = 123;
const { Wrapper, store } = getReduxStoreWithActionsListener({
...INITIAL_STORE_STATE_MOCK,
drawer: {
...initialStateFixture,
drawerName: 'search',
isOpen: true,
searchDrawerState: {
...initialStateFixture.searchDrawerState,
selectedSearchElement: 'search-tab',
},
},
bioEntity: {
data: [
{
searchQueryElement: 'search-tab',
loading: 'succeeded',
error: { name: '', message: '' },
data: [
{
...bioEntityContentFixture,
bioEntity: {
...bioEntityContentFixture.bioEntity,
fullName: null,
id: pinId,
model: ZERO,
},
},
],
},
],
loading: 'succeeded',
error: { message: '', name: '' },
},
});
const {
result: { current: onPinIconClick },
} = renderHook(() => useHandlePinIconClick(), {
wrapper: Wrapper,
});
onPinIconClick({ id: pinId });
it('should do not dispatch any action', () => {
const actions = store.getActions();
expect(actions).toStrictEqual([]);
});
});
describe('when pin is marker', () => {
const pinId = 'af57c6ce-8b83-47e4-a7eb-9511634c7c5f';
const { Wrapper, store } = getReduxStoreWithActionsListener({
...INITIAL_STORE_STATE_MOCK,
drawer: {
...initialStateFixture,
drawerName: 'search',
isOpen: true,
searchDrawerState: {
...initialStateFixture.searchDrawerState,
selectedSearchElement: 'search-tab',
},
},
bioEntity: {
data: [
{
searchQueryElement: 'search-tab',
loading: 'succeeded',
error: { name: '', message: '' },
data: [
{
...bioEntityContentFixture,
bioEntity: {
...bioEntityContentFixture.bioEntity,
fullName: null,
id: pinId,
model: ZERO,
},
},
],
},
],
loading: 'succeeded',
error: { message: '', name: '' },
},
});
const {
result: { current: onPinIconClick },
} = renderHook(() => useHandlePinIconClick(), {
wrapper: Wrapper,
});
onPinIconClick({ id: pinId });
it('should do not dispatch any action', () => {
const actions = store.getActions();
expect(actions).toStrictEqual([]);
});
});
describe('when all valid', () => {
const pinId = 123;
const { Wrapper, store } = getReduxStoreWithActionsListener({
...INITIAL_STORE_STATE_MOCK,
drawer: {
...initialStateFixture,
drawerName: 'search',
isOpen: true,
searchDrawerState: {
...initialStateFixture.searchDrawerState,
selectedSearchElement: 'search-tab-2',
},
},
bioEntity: {
data: [
{
searchQueryElement: 'search-tab',
loading: 'succeeded',
error: { name: '', message: '' },
data: [
{
...bioEntityContentFixture,
bioEntity: {
...bioEntityContentFixture.bioEntity,
fullName: null,
id: pinId,
model: ZERO,
},
},
],
},
],
loading: 'succeeded',
error: { message: '', name: '' },
},
});
const {
result: { current: onPinIconClick },
} = renderHook(() => useHandlePinIconClick(), {
wrapper: Wrapper,
});
onPinIconClick({ id: pinId });
it('should dispatch action', () => {
const actions = store.getActions();
expect(actions).toStrictEqual([{ payload: 'search-tab', type: 'drawer/selectTab' }]);
});
});
});
});
import { allBioEntitiesElementsIdsSelector } from '@/redux/bioEntity/bioEntity.selectors';
import { currentSelectedSearchElement } from '@/redux/drawer/drawer.selectors';
import { selectTab } from '@/redux/drawer/drawer.slice';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { useAppSelector } from '@/redux/hooks/useAppSelector';
import { PluginsEventBus as EventBus } from '@/services/pluginsManager/pluginsEventBus';
import { ClickedPinIcon } from '@/services/pluginsManager/pluginsEventBus/pluginsEventBus.types';
import isUUID from 'is-uuid';
import { useCallback, useEffect } from 'react';
export type OnPinIconClick = ({ id }: ClickedPinIcon) => void;
export const useHandlePinIconClick = (): OnPinIconClick => {
const dispatch = useAppDispatch();
const currentTab = useAppSelector(currentSelectedSearchElement);
const idsTabs = useAppSelector(allBioEntitiesElementsIdsSelector);
const onPinIconClick = useCallback(
({ id }: ClickedPinIcon): void => {
const newTab = idsTabs[id];
const isTabAlreadySelected = newTab === currentTab;
const isMarker = isUUID.anyNonNil(`${id}`);
if (!newTab || isTabAlreadySelected || isMarker) {
return;
}
dispatch(selectTab(idsTabs[id]));
},
[idsTabs, dispatch, currentTab],
);
useEffect(() => {
EventBus.addLocalListener('onPinIconClick', onPinIconClick);
return () => {
EventBus.removeLocalListener('onPinIconClick', onPinIconClick);
};
}, [onPinIconClick]);
return onPinIconClick;
};
import { DEFAULT_ZOOM, OPTIONS } from '@/constants/map';
import { searchDistanceValSelector } from '@/redux/configuration/configuration.selectors';
import { resultDrawerOpen } from '@/redux/drawer/drawer.selectors';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import {
mapDataLastZoomValue,
......@@ -14,12 +16,11 @@ import { Pixel } from 'ol/pixel';
import { useEffect, useRef } from 'react';
import { useSelector } from 'react-redux';
import { useDebouncedCallback } from 'use-debounce';
import { searchDistanceValSelector } from '@/redux/configuration/configuration.selectors';
import { resultDrawerOpen } from '@/redux/drawer/drawer.selectors';
import { onMapRightClick } from './mapRightClick/onMapRightClick';
import { onMapSingleClick } from './mapSingleClick/onMapSingleClick';
import { onMapPositionChange } from './onMapPositionChange';
import { onPointerMove } from './onPointerMove';
import { useHandlePinIconClick } from './pinIconClick/useHandlePinIconClick';
interface UseOlMapListenersInput {
view: View;
......@@ -37,6 +38,8 @@ export const useOlMapListeners = ({ view, mapInstance }: UseOlMapListenersInput)
const pixel = useRef<Pixel>([]);
const dispatch = useAppDispatch();
useHandlePinIconClick();
const handleRightClick = useDebouncedCallback(
onMapRightClick(mapSize, modelId, dispatch),
OPTIONS.clickPersistTime,
......
......@@ -11,7 +11,7 @@ export const PIN_SIZE = {
export const PINS_COLORS: Record<PinType, string> = {
drugs: '#F48C41',
chemicals: '#640CE3',
chemicals: '#008325',
bioEntity: '#106AD7',
};
......@@ -22,4 +22,6 @@ export const PINS_COLOR_WITH_NONE: Record<PinTypeWithNone, string> = {
export const LINE_COLOR = '#00AAFF';
export const TEXT_COLOR = '#FFFFFF';
export const LINE_WIDTH = 6;
import { SIZE_OF_EMPTY_ARRAY } from '@/constants/common';
import { rootSelector } from '@/redux/root/root.selectors';
import { ElementIdTabObj } from '@/types/elements';
import { MultiSearchData } from '@/types/fetchDataState';
import { BioEntity, BioEntityContent, MapModel } from '@/types/models';
import { createSelector } from '@reduxjs/toolkit';
import {
allChemicalsBioEntitesOfAllMapsSelector,
allChemicalsIdTabSelectorOfCurrentMap,
searchedChemicalsBioEntitesOfCurrentMapSelector,
} from '../chemicals/chemicals.selectors';
import { currentSelectedBioEntityIdSelector } from '../contextMenu/contextMenu.selector';
......@@ -14,6 +16,7 @@ import {
} from '../drawer/drawer.selectors';
import {
allDrugsBioEntitesOfAllMapsSelector,
allDrugsIdTabSelectorOfCurrentMap,
searchedDrugsBioEntitesOfCurrentMapSelector,
} from '../drugs/drugs.selectors';
import { currentModelIdSelector, modelsDataSelector } from '../models/models.selectors';
......@@ -97,6 +100,44 @@ export const searchedBioEntitesSelectorOfCurrentMap = createSelector(
},
);
export const allBioEntitesSelectorOfCurrentMap = createSelector(
bioEntitySelector,
currentModelIdSelector,
(bioEntities, currentModelId): BioEntity[] => {
if (!bioEntities) {
return [];
}
return (bioEntities?.data || [])
.map(({ data }) => data || [])
.flat()
.filter(({ bioEntity }) => bioEntity.model === currentModelId)
.map(({ bioEntity }) => bioEntity);
},
);
export const allBioEntitesIdTabSelectorOfCurrentMap = createSelector(
bioEntitySelector,
currentModelIdSelector,
(bioEntities, currentModelId): ElementIdTabObj => {
if (!bioEntities) {
return {};
}
return Object.fromEntries(
(bioEntities?.data || [])
.map(({ data, searchQueryElement }): [typeof data, string] => [data, searchQueryElement])
.map(([data, tab]) =>
(data || [])
.flat()
.filter(({ bioEntity }) => bioEntity.model === currentModelId)
.map(d => [d.bioEntity.id, tab]),
)
.flat(),
);
},
);
export const numberOfBioEntitiesSelector = createSelector(
bioEntitiesForSelectedSearchElement,
state => (state?.data ? state.data.length : SIZE_OF_EMPTY_ARRAY),
......@@ -134,6 +175,13 @@ export const allVisibleBioEntitiesSelector = createSelector(
},
);
export const allVisibleBioEntitiesIdsSelector = createSelector(
allVisibleBioEntitiesSelector,
(elements): (string | number)[] => {
return elements.map(e => e.id);
},
);
export const allContentBioEntitesSelectorOfAllMaps = createSelector(
bioEntitySelector,
(bioEntities): BioEntity[] => {
......@@ -157,6 +205,19 @@ export const allBioEntitiesSelector = createSelector(
},
);
export const allBioEntitiesElementsIdsSelector = createSelector(
allBioEntitesIdTabSelectorOfCurrentMap,
allChemicalsIdTabSelectorOfCurrentMap,
allDrugsIdTabSelectorOfCurrentMap,
(content, chemicals, drugs): ElementIdTabObj => {
return {
...content,
...chemicals,
...drugs,
};
},
);
export const currentDrawerBioEntitySelector = createSelector(
allBioEntitiesSelector,
currentSearchedBioEntityId,
......
import { SIZE_OF_EMPTY_ARRAY } from '@/constants/common';
import { rootSelector } from '@/redux/root/root.selectors';
import { ElementId, ElementIdTabObj, Tab } from '@/types/elements';
import { MultiSearchData } from '@/types/fetchDataState';
import { BioEntity, Chemical } from '@/types/models';
import { createSelector } from '@reduxjs/toolkit';
......@@ -41,6 +42,47 @@ export const searchedChemicalsBioEntitesOfCurrentMapSelector = createSelector(
},
);
export const allChemicalsBioEntitesOfCurrentMapSelector = createSelector(
chemicalsSelector,
currentModelIdSelector,
(chemicalsState, currentModelId): BioEntity[] => {
return (chemicalsState?.data || [])
.map(({ data }) => data || [])
.flat()
.map(({ targets }) => targets.map(({ targetElements }) => targetElements))
.flat()
.flat()
.filter(bioEntity => bioEntity.model === currentModelId);
},
);
export const allChemicalsIdTabSelectorOfCurrentMap = createSelector(
chemicalsSelector,
currentModelIdSelector,
(chemicalsState, currentModelId): ElementIdTabObj => {
if (!chemicalsState) {
return {};
}
return Object.fromEntries(
(chemicalsState?.data || [])
.map(({ data, searchQueryElement }): [typeof data, string] => [data, searchQueryElement])
.map(([data, tab]) =>
(data || []).map(({ targets }): [ElementId, Tab][] =>
targets
.map(({ targetElements }) => targetElements)
.flat()
.flat()
.filter(bioEntity => bioEntity.model === currentModelId)
.map(bioEntity => [bioEntity.id, tab]),
),
)
.flat()
.flat(),
);
},
);
export const allChemicalsBioEntitesOfAllMapsSelector = createSelector(
chemicalsSelector,
(chemicalsState): BioEntity[] => {
......
import { SIZE_OF_EMPTY_ARRAY } from '@/constants/common';
import { rootSelector } from '@/redux/root/root.selectors';
import { ElementId, ElementIdTabObj, Tab } from '@/types/elements';
import { MultiSearchData } from '@/types/fetchDataState';
import { BioEntity, Drug } from '@/types/models';
import { createSelector } from '@reduxjs/toolkit';
......@@ -55,6 +56,47 @@ export const searchedDrugsBioEntitesOfCurrentMapSelector = createSelector(
},
);
export const allDrugsBioEntitesOfCurrentMapSelector = createSelector(
drugsSelector,
currentModelIdSelector,
(drugsState, currentModelId): BioEntity[] => {
return (drugsState?.data || [])
.map(({ data }) => data || [])
.flat()
.map(({ targets }) => targets.map(({ targetElements }) => targetElements))
.flat()
.flat()
.filter(bioEntity => bioEntity.model === currentModelId);
},
);
export const allDrugsIdTabSelectorOfCurrentMap = createSelector(
drugsSelector,
currentModelIdSelector,
(drugsState, currentModelId): ElementIdTabObj => {
if (!drugsState) {
return {};
}
return Object.fromEntries(
(drugsState?.data || [])
.map(({ data, searchQueryElement }): [typeof data, string] => [data, searchQueryElement])
.map(([data, tab]) =>
(data || []).map(({ targets }): [ElementId, Tab][] =>
targets
.map(({ targetElements }) => targetElements)
.flat()
.flat()
.filter(bioEntity => bioEntity.model === currentModelId)
.map(bioEntity => [bioEntity.id, tab]),
),
)
.flat()
.flat(),
);
},
);
export const allDrugsBioEntitesOfAllMapsSelector = createSelector(
drugsSelector,
(drugsState): BioEntity[] => {
......
import { ZERO } from '@/constants/common';
import { QueryDataParams } from '@/types/query';
import { createSelector } from '@reduxjs/toolkit';
import { ZERO } from '@/constants/common';
import { mapDataSelector } from '../map/map.selectors';
import { perfectMatchSelector, searchValueSelector } from '../search/search.selectors';
import { activeOverlaysIdSelector } from '../overlayBioEntity/overlayBioEntity.selector';
import { activePluginsIdSelector } from '../plugins/plugins.selectors';
import { projectMainIdSelector } from '../project/project.selectors';
import { perfectMatchSelector, searchValueSelector } from '../search/search.selectors';
export const queryDataParamsSelector = createSelector(
searchValueSelector,
......
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