Skip to content
Snippets Groups Projects
Commit a852febf authored by mateusz-winiarczyk's avatar mateusz-winiarczyk
Browse files

feat(map): click far away from element still select this element (MIN-207)

parent 655d3f01
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...,!157feat(map): click far away from element still select this element (MIN-207)
Showing
with 953 additions and 141 deletions
import { AppDispatch, RootState } from '@/redux/store';
import {
InitialStoreState,
getReduxStoreWithActionsListener,
} from '@/utils/testing/getReduxStoreActionsListener';
import { fireEvent, render, screen } from '@testing-library/react';
import { act } from 'react-dom/test-utils';
import { MockStoreEnhanced } from 'redux-mock-store';
import { DRAWER_INITIAL_STATE } from '@/redux/drawer/drawer.constants';
import { ClearAnchorsButton } from './ClearAnchorsButton.component';
const renderComponent = (
initialStore: InitialStoreState = {},
): { store: MockStoreEnhanced<Partial<RootState>, AppDispatch> } => {
const { Wrapper, store } = getReduxStoreWithActionsListener(initialStore);
return (
render(
<Wrapper>
<ClearAnchorsButton />
</Wrapper>,
),
{
store,
}
);
};
describe('ClearAnchorsButton - component', () => {
it('should trigger clear actions on clear button click and close result drawer if it is open', () => {
const { store } = renderComponent({
drawer: {
...DRAWER_INITIAL_STATE,
drawerName: 'bio-entity',
isOpen: true,
},
});
const button = screen.getByTitle('Clear');
act(() => {
fireEvent.click(button);
});
const actions = store.getActions();
expect(actions).toEqual([
{ payload: undefined, type: 'drawer/closeDrawer' },
{ payload: undefined, type: 'contextMenu/closeContextMenu' },
{ payload: undefined, type: 'reactions/resetReactionsData' },
{ payload: undefined, type: 'search/clearSearchData' },
{ payload: undefined, type: 'bioEntityContents/clearBioEntitiesData' },
{ payload: undefined, type: 'drugs/clearDrugsData' },
{ payload: undefined, type: 'chemicals/clearChemicalsData' },
]);
});
it('should trigger clear actions on clear button click and not close result drawer if it is not open', () => {
const { store } = renderComponent({
drawer: {
...DRAWER_INITIAL_STATE,
drawerName: 'bio-entity',
isOpen: false,
},
});
const button = screen.getByTitle('Clear');
act(() => {
fireEvent.click(button);
});
const actions = store.getActions();
expect(actions).toEqual([
{ payload: undefined, type: 'contextMenu/closeContextMenu' },
{ payload: undefined, type: 'reactions/resetReactionsData' },
{ payload: undefined, type: 'search/clearSearchData' },
{ payload: undefined, type: 'bioEntityContents/clearBioEntitiesData' },
{ payload: undefined, type: 'drugs/clearDrugsData' },
{ payload: undefined, type: 'chemicals/clearChemicalsData' },
]);
});
});
import { clearBioEntitiesData } from '@/redux/bioEntity/bioEntity.slice';
import { clearChemicalsData } from '@/redux/chemicals/chemicals.slice';
import { closeContextMenu } from '@/redux/contextMenu/contextMenu.slice';
import { resultDrawerOpen } from '@/redux/drawer/drawer.selectors';
import { closeDrawer } from '@/redux/drawer/drawer.slice';
import { clearDrugsData } from '@/redux/drugs/drugs.slice';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { resetReactionsData } from '@/redux/reactions/reactions.slice';
import { clearSearchData } from '@/redux/search/search.slice';
import { Button } from '@/shared/Button';
import { Icon } from '@/shared/Icon';
import React from 'react';
import { useSelector } from 'react-redux';
export const ClearAnchorsButton = (): React.ReactNode => {
const dispatch = useAppDispatch();
const isResultDrawerOpen = useSelector(resultDrawerOpen);
const closeInterfaceElements = (): void => {
if (isResultDrawerOpen) {
dispatch(closeDrawer());
}
dispatch(closeContextMenu());
};
const resetData = (): void => {
// Reset reactions list to prevent keeping the old selected reaction rendered
dispatch(resetReactionsData());
// Reset search data to prevent invalid filtering of the click-search ()
dispatch(clearSearchData());
// Reset old pins data
dispatch(clearBioEntitiesData());
dispatch(clearDrugsData());
dispatch(clearChemicalsData());
};
const onClearAnchorsClick = (): void => {
closeInterfaceElements();
resetData();
};
return (
<Button
className="ml-2 hover:bg-transparent active:bg-transparent"
onClick={onClearAnchorsClick}
title="Clear"
variantStyles="quiet"
>
<Icon name="clear" />
</Button>
);
};
export { ClearAnchorsButton } from './ClearAnchorsButton.component';
...@@ -3,6 +3,7 @@ import { UserAvatar } from '@/components/FunctionalArea/TopBar/UserAvatar'; ...@@ -3,6 +3,7 @@ import { UserAvatar } from '@/components/FunctionalArea/TopBar/UserAvatar';
import { openOverlaysDrawer, openSubmapsDrawer } from '@/redux/drawer/drawer.slice'; import { openOverlaysDrawer, openSubmapsDrawer } from '@/redux/drawer/drawer.slice';
import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; import { useAppDispatch } from '@/redux/hooks/useAppDispatch';
import { Button } from '@/shared/Button'; import { Button } from '@/shared/Button';
import { ClearAnchorsButton } from './ClearAnchorsButton';
export const TopBar = (): JSX.Element => { export const TopBar = (): JSX.Element => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
...@@ -20,11 +21,12 @@ export const TopBar = (): JSX.Element => { ...@@ -20,11 +21,12 @@ export const TopBar = (): JSX.Element => {
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
<UserAvatar /> <UserAvatar />
<SearchBar /> <SearchBar />
<ClearAnchorsButton />
<Button <Button
icon="plus" icon="plus"
isIcon isIcon
isFrontIcon isFrontIcon
className="ml-8 mr-4" className="ml-5 mr-4"
onClick={onSubmapsClick} onClick={onSubmapsClick}
title="Submaps" title="Submaps"
> >
......
...@@ -14,7 +14,7 @@ export const onMapRightClick = ...@@ -14,7 +14,7 @@ export const onMapRightClick =
dispatch(handleDataReset); dispatch(handleDataReset);
dispatch(openContextMenu(pixel)); dispatch(openContextMenu(pixel));
const searchResults = await getSearchResults({ coordinate, mapSize, modelId }); const { searchResults } = await getSearchResults({ coordinate, mapSize, modelId });
if (!searchResults || searchResults.length === SIZE_OF_EMPTY_ARRAY) { if (!searchResults || searchResults.length === SIZE_OF_EMPTY_ARRAY) {
return; return;
} }
......
/* eslint-disable no-magic-numbers */
import { bioEntityContentFixture } from '@/models/fixtures/bioEntityContentsFixture';
import { findClosestBioEntityPoint } from './findClosestBioEntityPoint';
describe('findClosestBioEntityPoint', () => {
const bioEntityContents = [
{
...bioEntityContentFixture,
bioEntity: { ...bioEntityContentFixture.bioEntity, x: 10, y: 10, width: 20, height: 20 },
},
{
...bioEntityContentFixture,
bioEntity: { ...bioEntityContentFixture.bioEntity, x: 50, y: 50, width: 30, height: 30 },
},
];
const validPoint = { x: 15, y: 15 };
const invalidPoint = {
x: 500,
y: 300,
};
const searchDistance = '10';
const maxZoom = 5;
const zoom = 2;
it('should return the closest bioEntity within the search distance', () => {
const result = findClosestBioEntityPoint(
bioEntityContents,
searchDistance,
maxZoom,
zoom,
validPoint,
);
expect(result).toEqual(bioEntityContents[0]);
});
it('should return undefined if no matching bioEntity is found within the search distance', () => {
const result = findClosestBioEntityPoint(
bioEntityContents,
searchDistance,
maxZoom,
zoom,
invalidPoint,
);
expect(result).toBeUndefined();
});
});
import { Point as PointType } from '@/types/map';
import { BioEntityContent } from '@/types/models';
import { getMaxClickDistance } from './getMaxClickDistance';
export const findClosestBioEntityPoint = (
bioEntityContents: BioEntityContent[],
searchDistance: string,
maxZoom: number,
zoom: number,
point: PointType,
): BioEntityContent | undefined => {
const maxDistance = getMaxClickDistance(maxZoom, zoom, searchDistance);
const matchingBioEntityFound = bioEntityContents.find(bio => {
const { x, y, width, height } = bio.bioEntity;
const minX = x - maxDistance;
const maxX = x + width + maxDistance;
const minY = y - maxDistance;
const maxY = y + height + maxDistance;
const withinXRange = point.x >= minX && point.x <= maxX;
const withinYRange = point.y >= minY && point.y <= maxY;
return withinXRange && withinYRange;
});
return matchingBioEntityFound;
};
/* eslint-disable no-magic-numbers */
import { reactionsFixture } from '@/models/fixtures/reactionFixture';
import { findClosestReactionPoint } from './findClosestReactionPoint';
describe('findClosestReactionPoint', () => {
const reaction = {
...reactionsFixture[0],
lines: [{ start: { x: 0, y: 0 }, end: { x: 3, y: 4 }, type: 'START' }],
};
const validPoint = { x: 1, y: 1 };
const invalidPoint = {
x: 1115,
y: 2225,
};
const searchDistance = '10';
const maxZoom = 10;
const zoom = 5;
it('should return the matching line segment if a point within the search distance is found', () => {
const result = findClosestReactionPoint({
reaction,
searchDistance,
maxZoom,
zoom,
point: validPoint,
});
expect(result).toEqual(reaction.lines[0]);
});
it('should return undefined if no point within the search distance is found', () => {
const result = findClosestReactionPoint({
reaction,
searchDistance,
maxZoom,
zoom,
point: invalidPoint,
});
expect(result).toBeUndefined();
});
});
/* eslint-disable no-magic-numbers */
import { Point as PointType } from '@/types/map';
import { Reaction } from '@/types/models';
import { distance } from 'ol/coordinate';
import { LineString, Point } from 'ol/geom';
import { getMaxClickDistance } from './getMaxClickDistance';
type ReactionLine = Reaction['lines'][0];
type FindClosestReactionArgs = {
reaction: Reaction;
searchDistance: string;
maxZoom: number;
zoom: number;
point: PointType;
};
export const findClosestReactionPoint = ({
reaction,
searchDistance,
maxZoom,
zoom,
point,
}: FindClosestReactionArgs): ReactionLine | undefined => {
const maxDistance = getMaxClickDistance(maxZoom, zoom, searchDistance);
const clickedPoint = new Point([point.x, point.y]);
const closestLine = reaction.lines.find(line => {
const lineString = new LineString([
[line.start.x, line.start.y],
[line.end.x, line.end.y],
]);
const closestPointOnLine = lineString.getClosestPoint(clickedPoint.getCoordinates());
const distanceToLine = distance(closestPointOnLine, clickedPoint.getCoordinates());
return distanceToLine <= maxDistance;
});
return closestLine;
};
/* eslint-disable no-magic-numbers */
import { getMaxClickDistance } from './getMaxClickDistance';
describe('getMaxClickDistance', () => {
it.each([
[10, 5, '10', 320],
[10, 5, '20', 640],
[10, 2, '10', 2560],
[10, 3, '18', 2304],
])(
'should calculate the maximum click distance correctly',
(maxZoom, zoom, searchDistance, expected) => {
expect(getMaxClickDistance(maxZoom, zoom, searchDistance)).toBe(expected);
},
);
it.each([['invalid'], [''], [' ']])(
'should throw an error if the search distance "%s" is not a valid number',
invalidDistance => {
expect(() => getMaxClickDistance(10, 5, invalidDistance)).toThrow(
'Invalid search distance. Please provide a valid number.',
);
},
);
});
/* eslint-disable no-magic-numbers */
import { ZOOM_FACTOR } from '@/constants/common';
export const getMaxClickDistance = (
maxZoom: number,
zoom: number,
searchDistance: string,
): number => {
const distance = parseFloat(searchDistance);
if (typeof distance !== 'number' || Number.isNaN(distance)) {
throw new Error('Invalid search distance. Please provide a valid number.');
}
const zoomDiff = maxZoom - zoom;
const maxDistance = distance * ZOOM_FACTOR ** zoomDiff;
return maxDistance;
};
...@@ -36,7 +36,10 @@ describe('getSearchResults - util', () => { ...@@ -36,7 +36,10 @@ describe('getSearchResults - util', () => {
modelId, modelId,
}); });
expect(result).toEqual([ELEMENT_SEARCH_RESULT_MOCK_ALIAS]); expect(result).toEqual({
point,
searchResults: [ELEMENT_SEARCH_RESULT_MOCK_ALIAS],
});
}); });
}); });
...@@ -65,7 +68,10 @@ describe('getSearchResults - util', () => { ...@@ -65,7 +68,10 @@ describe('getSearchResults - util', () => {
modelId, modelId,
}); });
expect(result).toEqual([ELEMENT_SEARCH_RESULT_MOCK_REACTION]); expect(result).toEqual({
point,
searchResults: [ELEMENT_SEARCH_RESULT_MOCK_REACTION],
});
}); });
}); });
...@@ -96,7 +102,10 @@ describe('getSearchResults - util', () => { ...@@ -96,7 +102,10 @@ describe('getSearchResults - util', () => {
modelId, modelId,
}); });
expect(result).toEqual(undefined); expect(result).toEqual({
point,
searchResults: undefined,
});
}); });
}); });
}); });
import { MapSize } from '@/redux/map/map.types'; import { MapSize } from '@/redux/map/map.types';
import { Point } from '@/types/map';
import { ElementSearchResult } from '@/types/models'; import { ElementSearchResult } from '@/types/models';
import { latLngToPoint } from '@/utils/map/latLngToPoint'; import { latLngToPoint } from '@/utils/map/latLngToPoint';
import { getElementsByPoint } from '@/utils/search/getElementsByCoordinates'; import { getElementsByPoint } from '@/utils/search/getElementsByCoordinates';
...@@ -15,8 +16,16 @@ export const getSearchResults = async ({ ...@@ -15,8 +16,16 @@ export const getSearchResults = async ({
coordinate, coordinate,
mapSize, mapSize,
modelId, modelId,
}: GetSearchResultsInput): Promise<ElementSearchResult[] | undefined> => { }: GetSearchResultsInput): Promise<{
searchResults: ElementSearchResult[] | undefined;
point: Point;
}> => {
const [lng, lat] = toLonLat(coordinate); const [lng, lat] = toLonLat(coordinate);
const point = latLngToPoint([lat, lng], mapSize); const point = latLngToPoint([lat, lng], mapSize);
return getElementsByPoint({ point, currentModelId: modelId }); const searchResults = await getElementsByPoint({ point, currentModelId: modelId });
return {
searchResults,
point,
};
}; };
import { /* eslint-disable no-magic-numbers */
FIRST_ARRAY_ELEMENT, import { SIZE_OF_EMPTY_ARRAY } from '@/constants/common';
SECOND_ARRAY_ELEMENT,
SIZE_OF_EMPTY_ARRAY,
THIRD_ARRAY_ELEMENT,
} from '@/constants/common';
import { bioEntityResponseFixture } from '@/models/fixtures/bioEntityContentsFixture'; import { bioEntityResponseFixture } from '@/models/fixtures/bioEntityContentsFixture';
import { ELEMENT_SEARCH_RESULT_MOCK_ALIAS } from '@/models/mocks/elementSearchResultMock'; import { ELEMENT_SEARCH_RESULT_MOCK_ALIAS } from '@/models/mocks/elementSearchResultMock';
import { apiPath } from '@/redux/apiPath'; import { apiPath } from '@/redux/apiPath';
import { mockNetworkResponse } from '@/utils/mockNetworkResponse'; import { mockNetworkNewAPIResponse } from '@/utils/mockNetworkResponse';
import { getReduxStoreWithActionsListener } from '@/utils/testing/getReduxStoreActionsListener'; import { getReduxStoreWithActionsListener } from '@/utils/testing/getReduxStoreActionsListener';
import { waitFor } from '@testing-library/react'; import { waitFor } from '@testing-library/react';
import { HttpStatusCode } from 'axios'; import { HttpStatusCode } from 'axios';
import { searchFitBounds } from '@/services/pluginsManager/map/triggerSearch/searchFitBounds';
import { handleAliasResults } from './handleAliasResults'; import { handleAliasResults } from './handleAliasResults';
const mockedAxiosOldClient = mockNetworkResponse(); jest.mock('../../../../../../services/pluginsManager/map/triggerSearch/searchFitBounds');
const mockedAxiosClient = mockNetworkNewAPIResponse();
const SEARCH_CONFIG_MOCK = {
point: {
x: 500,
y: 700,
},
maxZoom: 9,
zoom: 5,
isResultDrawerOpen: false,
};
describe('handleAliasResults - util', () => { describe('handleAliasResults - util', () => {
const { store } = getReduxStoreWithActionsListener(); beforeEach(() => {
const { dispatch } = store; jest.clearAllMocks();
mockedAxiosOldClient
.onGet(
apiPath.getBioEntityContentsStringWithQuery({
searchQuery: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id.toString(),
isPerfectMatch: true,
}),
)
.reply(HttpStatusCode.Ok, bioEntityResponseFixture);
beforeAll(async () => {
handleAliasResults(
dispatch,
ELEMENT_SEARCH_RESULT_MOCK_ALIAS,
)(ELEMENT_SEARCH_RESULT_MOCK_ALIAS);
}); });
describe('when matching bioEntity not found', () => {
it('should clear bio entities and do not close drawer if result drawer is not open', async () => {
mockedAxiosClient
.onGet(
apiPath.getBioEntityContentsStringWithQuery({
searchQuery: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id.toString(),
isPerfectMatch: true,
}),
)
.reply(HttpStatusCode.Ok, bioEntityResponseFixture);
const { store } = getReduxStoreWithActionsListener();
const { dispatch } = store;
await handleAliasResults(dispatch, ELEMENT_SEARCH_RESULT_MOCK_ALIAS, {
...SEARCH_CONFIG_MOCK,
searchDistance: '10',
})(ELEMENT_SEARCH_RESULT_MOCK_ALIAS);
await waitFor(() => {
const actions = store.getActions();
expect(actions.length).toBeGreaterThan(SIZE_OF_EMPTY_ARRAY);
const actionTypes = actions.map(action => action.type);
expect(actionTypes).toEqual([
'project/getMultiBioEntity/pending',
'project/getBioEntityContents/pending',
'project/getBioEntityContents/fulfilled',
'entityNumber/addNumbersToEntityNumberData',
'project/getMultiBioEntity/fulfilled',
'bioEntityContents/clearBioEntitiesData',
]);
});
});
it('should clear bio entities and close drawer if result drawer is already open', async () => {
mockedAxiosClient
.onGet(
apiPath.getBioEntityContentsStringWithQuery({
searchQuery: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id.toString(),
isPerfectMatch: true,
}),
)
.reply(HttpStatusCode.Ok, bioEntityResponseFixture);
const { store } = getReduxStoreWithActionsListener();
const { dispatch } = store;
await handleAliasResults(dispatch, ELEMENT_SEARCH_RESULT_MOCK_ALIAS, {
...SEARCH_CONFIG_MOCK,
searchDistance: '10',
isResultDrawerOpen: true,
})(ELEMENT_SEARCH_RESULT_MOCK_ALIAS);
await waitFor(() => {
const actions = store.getActions();
expect(actions.length).toBeGreaterThan(SIZE_OF_EMPTY_ARRAY);
it('should run selectTab as first action', async () => { const actionTypes = actions.map(action => action.type);
await waitFor(() => {
const actions = store.getActions(); expect(actionTypes).toEqual([
expect(actions.length).toBeGreaterThan(SIZE_OF_EMPTY_ARRAY); 'project/getMultiBioEntity/pending',
expect(actions[FIRST_ARRAY_ELEMENT].type).toEqual('drawer/selectTab'); 'project/getBioEntityContents/pending',
'project/getBioEntityContents/fulfilled',
'entityNumber/addNumbersToEntityNumberData',
'project/getMultiBioEntity/fulfilled',
'drawer/closeDrawer',
'bioEntityContents/clearBioEntitiesData',
]);
});
}); });
}); });
describe('when matching bioEntity found', () => {
it('should select tab and open bio entity drawer', async () => {
mockedAxiosClient
.onGet(
apiPath.getBioEntityContentsStringWithQuery({
searchQuery: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id.toString(),
isPerfectMatch: true,
}),
)
.reply(HttpStatusCode.Ok, {
...bioEntityResponseFixture,
content: [
{
...bioEntityResponseFixture.content[0],
bioEntity: {
...bioEntityResponseFixture.content[0].bioEntity,
x: 500,
y: 700,
width: 50,
height: 50,
},
},
],
});
const { store } = getReduxStoreWithActionsListener();
const { dispatch } = store;
await handleAliasResults(
dispatch,
ELEMENT_SEARCH_RESULT_MOCK_ALIAS,
SEARCH_CONFIG_MOCK,
)(ELEMENT_SEARCH_RESULT_MOCK_ALIAS);
await waitFor(() => {
const actions = store.getActions();
expect(actions.length).toBeGreaterThan(SIZE_OF_EMPTY_ARRAY);
it('should run openBioEntityDrawerById as second action', async () => { const actionTypes = actions.map(action => action.type);
await waitFor(() => {
const actions = store.getActions(); expect(actionTypes).toEqual([
expect(actions.length).toBeGreaterThan(SIZE_OF_EMPTY_ARRAY); 'project/getMultiBioEntity/pending',
expect(actions[SECOND_ARRAY_ELEMENT].type).toEqual('drawer/openBioEntityDrawerById'); 'project/getBioEntityContents/pending',
'project/getBioEntityContents/fulfilled',
'entityNumber/addNumbersToEntityNumberData',
'project/getMultiBioEntity/fulfilled',
'drawer/selectTab',
'drawer/openBioEntityDrawerById',
]);
});
}); });
}); });
it('should run getMultiBioEntity as third action', async () => { describe('when searchDistance is not provided', () => {
await waitFor(() => { it('should select tab and open drawer without clearing bio entities', async () => {
const actions = store.getActions(); mockedAxiosClient
expect(actions.length).toBeGreaterThan(SIZE_OF_EMPTY_ARRAY); .onGet(
expect(actions[THIRD_ARRAY_ELEMENT].type).toEqual('project/getMultiBioEntity/pending'); apiPath.getBioEntityContentsStringWithQuery({
searchQuery: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id.toString(),
isPerfectMatch: true,
}),
)
.reply(HttpStatusCode.Ok, bioEntityResponseFixture);
const { store } = getReduxStoreWithActionsListener();
const { dispatch } = store;
await handleAliasResults(dispatch, ELEMENT_SEARCH_RESULT_MOCK_ALIAS, {
...SEARCH_CONFIG_MOCK,
isResultDrawerOpen: true,
})(ELEMENT_SEARCH_RESULT_MOCK_ALIAS);
await waitFor(() => {
const actions = store.getActions();
expect(actions.length).toBeGreaterThan(SIZE_OF_EMPTY_ARRAY);
const actionTypes = actions.map(action => action.type);
expect(actionTypes).toEqual([
'project/getMultiBioEntity/pending',
'project/getBioEntityContents/pending',
'project/getBioEntityContents/fulfilled',
'entityNumber/addNumbersToEntityNumberData',
'project/getMultiBioEntity/fulfilled',
'drawer/selectTab',
'drawer/openBioEntityDrawerById',
]);
});
});
describe('fitBounds after search', () => {
it('should fit bounds after search when hasFitBounds is true', async () => {
mockedAxiosClient
.onGet(
apiPath.getBioEntityContentsStringWithQuery({
searchQuery: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id.toString(),
isPerfectMatch: true,
}),
)
.reply(HttpStatusCode.Ok, bioEntityResponseFixture);
const { store } = getReduxStoreWithActionsListener();
const { dispatch } = store;
await handleAliasResults(dispatch, ELEMENT_SEARCH_RESULT_MOCK_ALIAS, {
...SEARCH_CONFIG_MOCK,
hasFitBounds: true,
})(ELEMENT_SEARCH_RESULT_MOCK_ALIAS);
await waitFor(() => {
expect(searchFitBounds).toHaveBeenCalled();
});
});
it('should not fit bounds after search when hasFitBounds is false', async () => {
mockedAxiosClient
.onGet(
apiPath.getBioEntityContentsStringWithQuery({
searchQuery: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id.toString(),
isPerfectMatch: true,
}),
)
.reply(HttpStatusCode.Ok, bioEntityResponseFixture);
const { store } = getReduxStoreWithActionsListener();
const { dispatch } = store;
await handleAliasResults(dispatch, ELEMENT_SEARCH_RESULT_MOCK_ALIAS, {
...SEARCH_CONFIG_MOCK,
hasFitBounds: false,
})(ELEMENT_SEARCH_RESULT_MOCK_ALIAS);
await waitFor(() => {
expect(searchFitBounds).not.toHaveBeenCalled();
});
});
}); });
}); });
}); });
import { getMultiBioEntity } from '@/redux/bioEntity/bioEntity.thunks'; import { getMultiBioEntity } from '@/redux/bioEntity/bioEntity.thunks';
import { openBioEntityDrawerById, selectTab } from '@/redux/drawer/drawer.slice'; import { closeDrawer, openBioEntityDrawerById, selectTab } from '@/redux/drawer/drawer.slice';
import { AppDispatch } from '@/redux/store'; import { AppDispatch } from '@/redux/store';
import { searchFitBounds } from '@/services/pluginsManager/map/triggerSearch/searchFitBounds'; import { searchFitBounds } from '@/services/pluginsManager/map/triggerSearch/searchFitBounds';
import { ElementSearchResult } from '@/types/models'; import { ElementSearchResult } from '@/types/models';
import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus'; import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus';
import { clearBioEntitiesData } from '@/redux/bioEntity/bioEntity.slice';
import { Point } from '@/types/map';
import { findClosestBioEntityPoint } from './findClosestBioEntityPoint';
type SearchConfig = {
point: Point;
searchDistance?: string;
maxZoom: number;
zoom: number;
hasFitBounds?: boolean;
isResultDrawerOpen?: boolean;
};
/* prettier-ignore */ /* prettier-ignore */
export const handleAliasResults = export const handleAliasResults =
(dispatch: AppDispatch, closestSearchResult: ElementSearchResult, hasFitBounds?: boolean, fitBoundsZoom?: number) => (dispatch: AppDispatch, closestSearchResult: ElementSearchResult, { hasFitBounds, maxZoom, point, searchDistance, zoom, isResultDrawerOpen }: SearchConfig) =>
async ({ id }: ElementSearchResult): Promise<void> => { async ({ id }: ElementSearchResult): Promise<void> => {
const bioEntityContents = await dispatch(
dispatch(selectTab(`${id}`));
dispatch(openBioEntityDrawerById(id));
dispatch(
getMultiBioEntity({ getMultiBioEntity({
searchQueries: [id.toString()], searchQueries: [id.toString()],
isPerfectMatch: true isPerfectMatch: true
}), }),
) ).unwrap();
.unwrap().then((bioEntityContents) => {
PluginsEventBus.dispatchEvent('onSearch', { if (searchDistance) {
type: 'bioEntity',
searchValues: [closestSearchResult],
results: [bioEntityContents],
});
if (hasFitBounds) { const matchingBioEntityFound = findClosestBioEntityPoint(bioEntityContents, searchDistance, maxZoom, zoom, point);
searchFitBounds(fitBoundsZoom);
if (!matchingBioEntityFound) {
if (isResultDrawerOpen) {
dispatch(closeDrawer());
} }
});
dispatch(clearBioEntitiesData());
return;
}
}
dispatch(selectTab(`${id}`));
dispatch(openBioEntityDrawerById(id));
PluginsEventBus.dispatchEvent('onSearch', {
type: 'bioEntity',
searchValues: [closestSearchResult],
results: [bioEntityContents],
});
if (hasFitBounds) {
searchFitBounds();
}
}; };
...@@ -12,89 +12,258 @@ import { mockNetworkNewAPIResponse, mockNetworkResponse } from '@/utils/mockNetw ...@@ -12,89 +12,258 @@ import { mockNetworkNewAPIResponse, mockNetworkResponse } from '@/utils/mockNetw
import { getReduxStoreWithActionsListener } from '@/utils/testing/getReduxStoreActionsListener'; import { getReduxStoreWithActionsListener } from '@/utils/testing/getReduxStoreActionsListener';
import { HttpStatusCode } from 'axios'; import { HttpStatusCode } from 'axios';
import { handleReactionResults } from './handleReactionResults'; import { handleReactionResults } from './handleReactionResults';
import * as findClosestReactionPoint from './findClosestReactionPoint';
const mockedAxiosOldClient = mockNetworkResponse(); const mockedAxiosOldClient = mockNetworkResponse();
const mockedAxiosNewClient = mockNetworkNewAPIResponse(); const mockedAxiosNewClient = mockNetworkNewAPIResponse();
jest.mock('./findClosestReactionPoint', () => ({
__esModule: true,
...jest.requireActual('./findClosestReactionPoint'),
}));
const findClosestReactionPointSpy = jest.spyOn(
findClosestReactionPoint,
'findClosestReactionPoint',
);
const SEARCH_CONFIG_MOCK = {
point: {
x: 200,
y: 3012,
},
maxZoom: 9,
zoom: 3,
isResultDrawerOpen: false,
};
describe('handleReactionResults - util', () => { describe('handleReactionResults - util', () => {
const { store } = getReduxStoreWithActionsListener({ const searchDistance = '10';
...INITIAL_STORE_STATE_MOCK,
});
const { dispatch } = store;
mockedAxiosNewClient
.onGet(
apiPath.getBioEntityContentsStringWithQuery({
searchQuery: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id.toString(),
isPerfectMatch: true,
}),
)
.reply(HttpStatusCode.Ok, bioEntityResponseFixture);
mockedAxiosOldClient
.onGet(apiPath.getReactionsWithIds([ELEMENT_SEARCH_RESULT_MOCK_REACTION.id]))
.reply(HttpStatusCode.Ok, [
{
...reactionsFixture[0],
reactants: [],
products: [],
modifiers: [
{
...reactionsFixture[0].modifiers[0],
aliasId: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id,
},
],
},
]);
beforeEach(async () => {
await handleReactionResults(
dispatch,
ELEMENT_SEARCH_RESULT_MOCK_REACTION,
)(ELEMENT_SEARCH_RESULT_MOCK_REACTION);
});
it('should run getReactionsByIds as first action', () => { beforeEach(() => {
const actions = store.getActions(); jest.clearAllMocks();
expect(actions.length).toBeGreaterThan(SIZE_OF_EMPTY_ARRAY);
expect(actions[0].type).toEqual('reactions/getByIds/pending');
expect(actions[1].type).toEqual('reactions/getByIds/fulfilled');
}); });
it('should run openReactionDrawerById to empty array as third action', () => { describe('actions', () => {
const actions = store.getActions(); const { store } = getReduxStoreWithActionsListener({
expect(actions.length).toBeGreaterThan(SIZE_OF_EMPTY_ARRAY); ...INITIAL_STORE_STATE_MOCK,
expect(actions[2].type).toEqual('drawer/openReactionDrawerById'); });
expect(actions[2].payload).toEqual(reactionsFixture[FIRST_ARRAY_ELEMENT].id); const { dispatch } = store;
});
it('should run select tab as fourth action', () => { mockedAxiosNewClient
const actions = store.getActions(); .onGet(
expect(actions.length).toBeGreaterThan(SIZE_OF_EMPTY_ARRAY); apiPath.getBioEntityContentsStringWithQuery({
expect(actions[3].type).toEqual('drawer/selectTab'); searchQuery: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id.toString(),
}); isPerfectMatch: true,
}),
)
.reply(HttpStatusCode.Ok, bioEntityResponseFixture);
mockedAxiosOldClient
.onGet(apiPath.getReactionsWithIds([ELEMENT_SEARCH_RESULT_MOCK_REACTION.id]))
.reply(HttpStatusCode.Ok, [
{
...reactionsFixture[0],
reactants: [],
products: [],
modifiers: [
{
...reactionsFixture[0].modifiers[0],
aliasId: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id,
},
],
},
]);
it('should run getMultiBioEntity to empty array as fifth action', () => { beforeEach(async () => {
const actions = store.getActions(); await handleReactionResults(
expect(actions.length).toBeGreaterThan(SIZE_OF_EMPTY_ARRAY); dispatch,
expect(actions[4].type).toEqual('project/getMultiBioEntity/pending'); ELEMENT_SEARCH_RESULT_MOCK_REACTION,
)(ELEMENT_SEARCH_RESULT_MOCK_REACTION);
});
it('should run getReactionsByIds as first action', () => {
const actions = store.getActions();
expect(actions.length).toBeGreaterThan(SIZE_OF_EMPTY_ARRAY);
expect(actions[0].type).toEqual('reactions/getByIds/pending');
expect(actions[1].type).toEqual('reactions/getByIds/fulfilled');
});
it('should run openReactionDrawerById to empty array as third action', () => {
const actions = store.getActions();
expect(actions.length).toBeGreaterThan(SIZE_OF_EMPTY_ARRAY);
expect(actions[2].type).toEqual('drawer/openReactionDrawerById');
expect(actions[2].payload).toEqual(reactionsFixture[FIRST_ARRAY_ELEMENT].id);
});
it('should run select tab as fourth action', () => {
const actions = store.getActions();
expect(actions.length).toBeGreaterThan(SIZE_OF_EMPTY_ARRAY);
expect(actions[3].type).toEqual('drawer/selectTab');
});
it('should run getMultiBioEntity to empty array as fifth action', () => {
const actions = store.getActions();
expect(actions.length).toBeGreaterThan(SIZE_OF_EMPTY_ARRAY);
expect(actions[4].type).toEqual('project/getMultiBioEntity/pending');
});
it('should run getBioEntityContents as sixth action', () => {
const actions = store.getActions();
expect(actions.length).toBeGreaterThan(SIZE_OF_EMPTY_ARRAY);
expect(actions[5].type).toEqual('project/getBioEntityContents/pending');
});
it('should run getBioEntityContents fullfilled as seventh action', () => {
const actions = store.getActions();
expect(actions.length).toBeGreaterThan(SIZE_OF_EMPTY_ARRAY);
expect(actions[6].type).toEqual('project/getBioEntityContents/fulfilled');
});
it('should run addNumbersToEntityNumberData as eighth action', () => {
const actions = store.getActions();
expect(actions.length).toBeGreaterThan(SIZE_OF_EMPTY_ARRAY);
expect(actions[7].type).toEqual('entityNumber/addNumbersToEntityNumberData');
});
}); });
describe('when search config provided but search distance is not provided', () => {
const { store } = getReduxStoreWithActionsListener();
const { dispatch } = store;
it('should not find closest reaction', async () => {
await handleReactionResults(
dispatch,
ELEMENT_SEARCH_RESULT_MOCK_REACTION,
SEARCH_CONFIG_MOCK,
)(ELEMENT_SEARCH_RESULT_MOCK_REACTION);
it('should run getBioEntityContents as sixth action', () => { expect(findClosestReactionPointSpy).not.toHaveBeenCalled();
const actions = store.getActions(); });
expect(actions.length).toBeGreaterThan(SIZE_OF_EMPTY_ARRAY);
expect(actions[5].type).toEqual('project/getBioEntityContents/pending');
}); });
it('should run getBioEntityContents fullfilled as seventh action', () => { describe('when search config provided and matching reaction not found', () => {
const actions = store.getActions(); mockedAxiosNewClient
expect(actions.length).toBeGreaterThan(SIZE_OF_EMPTY_ARRAY); .onGet(
expect(actions[6].type).toEqual('project/getBioEntityContents/fulfilled'); apiPath.getBioEntityContentsStringWithQuery({
searchQuery: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id.toString(),
isPerfectMatch: true,
}),
)
.reply(HttpStatusCode.Ok, bioEntityResponseFixture);
mockedAxiosOldClient
.onGet(apiPath.getReactionsWithIds([ELEMENT_SEARCH_RESULT_MOCK_REACTION.id]))
.reply(HttpStatusCode.Ok, [
{
...reactionsFixture[0],
reactants: [],
products: [],
modifiers: [
{
...reactionsFixture[0].modifiers[0],
aliasId: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id,
},
],
},
]);
const invalidPoint = {
x: 991,
y: 612,
};
it('should close drawer and reset data if result drawer open', async () => {
const { store } = getReduxStoreWithActionsListener();
await handleReactionResults(store.dispatch, ELEMENT_SEARCH_RESULT_MOCK_REACTION, {
...SEARCH_CONFIG_MOCK,
searchDistance,
point: invalidPoint,
isResultDrawerOpen: true,
})(ELEMENT_SEARCH_RESULT_MOCK_REACTION);
const actions = store.getActions();
const acionTypes = actions.map(action => action.type);
expect(acionTypes).toStrictEqual([
'reactions/getByIds/pending',
'reactions/getByIds/fulfilled',
'drawer/closeDrawer',
'reactions/resetReactionsData',
'bioEntityContents/clearBioEntitiesData',
]);
});
it('should only reset data if result drawer is closed', async () => {
const { store } = getReduxStoreWithActionsListener();
const dispatchSpy = jest.spyOn(store, 'dispatch');
await handleReactionResults(store.dispatch, ELEMENT_SEARCH_RESULT_MOCK_REACTION, {
...SEARCH_CONFIG_MOCK,
searchDistance,
point: invalidPoint,
isResultDrawerOpen: false,
})(ELEMENT_SEARCH_RESULT_MOCK_REACTION);
expect(dispatchSpy).toHaveBeenCalledWith({
payload: undefined,
type: 'reactions/resetReactionsData',
});
expect(dispatchSpy).toHaveBeenCalledWith({
payload: undefined,
type: 'bioEntityContents/clearBioEntitiesData',
});
});
}); });
describe('when search config provided and matching reaction found', () => {
const reaction = {
...reactionsFixture[0],
products: [],
reactants: [],
modifiers: [],
lines: [{ start: { x: 0, y: 0 }, end: { x: 3, y: 4 }, type: 'START' }],
};
const point = { x: 1, y: 1 };
const maxZoom = 10;
const zoom = 5;
it('should open reaction drawer and fetch bio entities', async () => {
const { store } = getReduxStoreWithActionsListener();
mockedAxiosNewClient
.onGet(
apiPath.getBioEntityContentsStringWithQuery({
searchQuery: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id.toString(),
isPerfectMatch: true,
}),
)
.reply(HttpStatusCode.Ok, []);
mockedAxiosOldClient
.onGet(apiPath.getReactionsWithIds([ELEMENT_SEARCH_RESULT_MOCK_REACTION.id]))
.reply(HttpStatusCode.Ok, [reaction]);
await handleReactionResults(store.dispatch, ELEMENT_SEARCH_RESULT_MOCK_REACTION, {
searchDistance,
maxZoom,
zoom,
point,
isResultDrawerOpen: false,
})(ELEMENT_SEARCH_RESULT_MOCK_REACTION);
const actions = store.getActions();
const acionTypes = actions.map(action => action.type);
it('should run addNumbersToEntityNumberData as eighth action', () => { expect(acionTypes).toStrictEqual([
const actions = store.getActions(); 'reactions/getByIds/pending',
expect(actions.length).toBeGreaterThan(SIZE_OF_EMPTY_ARRAY); 'reactions/getByIds/fulfilled',
expect(actions[7].type).toEqual('entityNumber/addNumbersToEntityNumberData'); 'drawer/openReactionDrawerById',
'drawer/selectTab',
'project/getMultiBioEntity/pending',
'entityNumber/addNumbersToEntityNumberData',
'project/getMultiBioEntity/fulfilled',
]);
});
}); });
}); });
...@@ -3,14 +3,26 @@ import { getMultiBioEntity } from '@/redux/bioEntity/bioEntity.thunks'; ...@@ -3,14 +3,26 @@ import { getMultiBioEntity } from '@/redux/bioEntity/bioEntity.thunks';
import { openReactionDrawerById, selectTab } from '@/redux/drawer/drawer.slice'; import { openReactionDrawerById, selectTab } from '@/redux/drawer/drawer.slice';
import { getReactionsByIds } from '@/redux/reactions/reactions.thunks'; import { getReactionsByIds } from '@/redux/reactions/reactions.thunks';
import { AppDispatch } from '@/redux/store'; import { AppDispatch } from '@/redux/store';
import { searchFitBounds } from '@/services/pluginsManager/map/triggerSearch/searchFitBounds';
import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus';
import { ElementSearchResult, Reaction } from '@/types/models'; import { ElementSearchResult, Reaction } from '@/types/models';
import { PayloadAction } from '@reduxjs/toolkit'; import { PayloadAction } from '@reduxjs/toolkit';
import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus';
import { Point } from '@/types/map';
import { searchFitBounds } from '@/services/pluginsManager/map/triggerSearch/searchFitBounds';
import { findClosestReactionPoint } from './findClosestReactionPoint';
import { handleReactionSearchClickFailure } from './handleReactionSearchClickFailure';
type SearchConfig = {
point: Point;
searchDistance?: string;
maxZoom: number;
zoom: number;
hasFitBounds?: boolean;
isResultDrawerOpen: boolean;
};
/* prettier-ignore */ /* prettier-ignore */
export const handleReactionResults = export const handleReactionResults =
(dispatch: AppDispatch, closestSearchResult: ElementSearchResult, hasFitBounds?: boolean, fitBoundsZoom?: number) => (dispatch: AppDispatch, closestSearchResult: ElementSearchResult, searchConfig?: SearchConfig) =>
async ({ id }: ElementSearchResult): Promise<void> => { async ({ id }: ElementSearchResult): Promise<void> => {
const data = await dispatch(getReactionsByIds([id])) as PayloadAction<Reaction[] | undefined>; const data = await dispatch(getReactionsByIds([id])) as PayloadAction<Reaction[] | undefined>;
const payload = data?.payload; const payload = data?.payload;
...@@ -19,6 +31,20 @@ export const handleReactionResults = ...@@ -19,6 +31,20 @@ export const handleReactionResults =
} }
const reaction = payload[FIRST_ARRAY_ELEMENT]; const reaction = payload[FIRST_ARRAY_ELEMENT];
if (searchConfig && searchConfig.searchDistance) {
const { maxZoom, point, searchDistance, zoom, isResultDrawerOpen } = searchConfig;
const matchingReactionFound = findClosestReactionPoint({
reaction, searchDistance, maxZoom, zoom, point
});
if (!matchingReactionFound) {
handleReactionSearchClickFailure(dispatch, isResultDrawerOpen);
return;
}
}
const { products, reactants, modifiers } = reaction; const { products, reactants, modifiers } = reaction;
const productsIds = products.map(p => p.aliasId); const productsIds = products.map(p => p.aliasId);
const reactantsIds = reactants.map(r => r.aliasId); const reactantsIds = reactants.map(r => r.aliasId);
...@@ -26,6 +52,7 @@ export const handleReactionResults = ...@@ -26,6 +52,7 @@ export const handleReactionResults =
const bioEntitiesIds = [...productsIds, ...reactantsIds, ...modifiersIds].map(identifier => String(identifier)); const bioEntitiesIds = [...productsIds, ...reactantsIds, ...modifiersIds].map(identifier => String(identifier));
dispatch(openReactionDrawerById(reaction.id)); dispatch(openReactionDrawerById(reaction.id));
dispatch(selectTab('')); dispatch(selectTab(''));
await dispatch( await dispatch(
getMultiBioEntity({ getMultiBioEntity({
...@@ -39,8 +66,9 @@ export const handleReactionResults = ...@@ -39,8 +66,9 @@ export const handleReactionResults =
results: [bioEntityContents], results: [bioEntityContents],
}); });
if (hasFitBounds) { if (searchConfig && searchConfig.hasFitBounds) {
searchFitBounds(fitBoundsZoom); searchFitBounds();
} }
}); });
}; };
import { AppDispatch } from '@/redux/store';
import { closeDrawer } from '@/redux/drawer/drawer.slice';
import { resetReactionsData } from '@/redux/reactions/reactions.slice';
import { clearBioEntitiesData } from '@/redux/bioEntity/bioEntity.slice';
export const handleReactionSearchClickFailure = (
dispatch: AppDispatch,
isResultDrawerOpen: boolean,
): void => {
if (isResultDrawerOpen) {
dispatch(closeDrawer());
}
dispatch(resetReactionsData());
dispatch(clearBioEntitiesData());
};
...@@ -19,6 +19,13 @@ jest.mock('./handleReactionResults', () => ({ ...@@ -19,6 +19,13 @@ jest.mock('./handleReactionResults', () => ({
const handleAliasResultsSpy = jest.spyOn(handleAliasResults, 'handleAliasResults'); const handleAliasResultsSpy = jest.spyOn(handleAliasResults, 'handleAliasResults');
const handleReactionResultsSpy = jest.spyOn(handleReactionResults, 'handleReactionResults'); const handleReactionResultsSpy = jest.spyOn(handleReactionResults, 'handleReactionResults');
const POINT_MOCK = {
x: 1323,
y: 2000,
};
const ZOOM_MOCK = 3;
const MAX_ZOOM_MOCK = 9;
describe('handleSearchResultAction - util', () => { describe('handleSearchResultAction - util', () => {
const dispatch = jest.fn(); const dispatch = jest.fn();
...@@ -30,7 +37,14 @@ describe('handleSearchResultAction - util', () => { ...@@ -30,7 +37,14 @@ describe('handleSearchResultAction - util', () => {
const searchResults = [ELEMENT_SEARCH_RESULT_MOCK_ALIAS]; const searchResults = [ELEMENT_SEARCH_RESULT_MOCK_ALIAS];
it('should fire handleAliasResults', async () => { it('should fire handleAliasResults', async () => {
await handleSearchResultAction({ searchResults, dispatch }); await handleSearchResultAction({
searchResults,
dispatch,
maxZoom: MAX_ZOOM_MOCK,
isResultDrawerOpen: false,
point: POINT_MOCK,
zoom: ZOOM_MOCK,
});
expect(handleAliasResultsSpy).toBeCalled(); expect(handleAliasResultsSpy).toBeCalled();
}); });
}); });
...@@ -39,7 +53,14 @@ describe('handleSearchResultAction - util', () => { ...@@ -39,7 +53,14 @@ describe('handleSearchResultAction - util', () => {
const searchResults = [ELEMENT_SEARCH_RESULT_MOCK_REACTION]; const searchResults = [ELEMENT_SEARCH_RESULT_MOCK_REACTION];
it('should fire handleReactionResults', async () => { it('should fire handleReactionResults', async () => {
await handleSearchResultAction({ searchResults, dispatch }); await handleSearchResultAction({
searchResults,
dispatch,
maxZoom: MAX_ZOOM_MOCK,
isResultDrawerOpen: false,
point: POINT_MOCK,
zoom: ZOOM_MOCK,
});
expect(handleReactionResultsSpy).toBeCalled(); expect(handleReactionResultsSpy).toBeCalled();
}); });
}); });
......
...@@ -2,21 +2,30 @@ import { FIRST_ARRAY_ELEMENT } from '@/constants/common'; ...@@ -2,21 +2,30 @@ import { FIRST_ARRAY_ELEMENT } from '@/constants/common';
import { AppDispatch } from '@/redux/store'; import { AppDispatch } from '@/redux/store';
import { ElementSearchResult } from '@/types/models'; import { ElementSearchResult } from '@/types/models';
import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus'; import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus';
import { Point } from '@/types/map';
import { handleAliasResults } from './handleAliasResults'; import { handleAliasResults } from './handleAliasResults';
import { handleReactionResults } from './handleReactionResults'; import { handleReactionResults } from './handleReactionResults';
interface HandleSearchResultActionInput { interface HandleSearchResultActionInput {
searchResults: ElementSearchResult[]; searchResults: ElementSearchResult[];
dispatch: AppDispatch; dispatch: AppDispatch;
point: Point;
searchDistance?: string;
maxZoom: number;
zoom: number;
hasFitBounds?: boolean; hasFitBounds?: boolean;
fitBoundsZoom?: number; isResultDrawerOpen: boolean;
} }
export const handleSearchResultAction = async ({ export const handleSearchResultAction = async ({
searchResults, searchResults,
dispatch, dispatch,
point,
searchDistance,
maxZoom,
zoom,
hasFitBounds, hasFitBounds,
fitBoundsZoom, isResultDrawerOpen,
}: HandleSearchResultActionInput): Promise<void> => { }: HandleSearchResultActionInput): Promise<void> => {
const closestSearchResult = searchResults[FIRST_ARRAY_ELEMENT]; const closestSearchResult = searchResults[FIRST_ARRAY_ELEMENT];
const { type } = closestSearchResult; const { type } = closestSearchResult;
...@@ -25,7 +34,14 @@ export const handleSearchResultAction = async ({ ...@@ -25,7 +34,14 @@ export const handleSearchResultAction = async ({
REACTION: handleReactionResults, REACTION: handleReactionResults,
}[type]; }[type];
await action(dispatch, closestSearchResult, hasFitBounds, fitBoundsZoom)(closestSearchResult); await action(dispatch, closestSearchResult, {
point,
searchDistance,
maxZoom,
zoom,
hasFitBounds,
isResultDrawerOpen,
})(closestSearchResult);
if (type === 'ALIAS') { if (type === 'ALIAS') {
PluginsEventBus.dispatchEvent('onBioEntityClick', closestSearchResult); PluginsEventBus.dispatchEvent('onBioEntityClick', closestSearchResult);
......
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