diff --git a/docs/plugins/search.md b/docs/plugins/search.md
new file mode 100644
index 0000000000000000000000000000000000000000..05856f1703724d85759ff8752a2d5ebad04595b7
--- /dev/null
+++ b/docs/plugins/search.md
@@ -0,0 +1,49 @@
+### Search
+
+Search can be done by query or coordinates. To search, plugins can use the `triggerSearch` method in `window.minerva.map` object available globally.
+
+**Search by query:**
+If we want to search using a query, we need to provide an object with the following properties as an argument:
+
+- query: this should be the search string.
+- perfectSearch: this property indicates whether results should be a perfect match or not. Its value should be a boolean type. This property is optional, and by default, its value is `false`.
+- fitBounds: should the map be resized to the rectangle fitting all results. Its value should be a boolean type. This property is optional, and by default, its value is `false`.
+
+##### Example of search by query:
+
+```javascript
+window.minerva.map.triggerSearch({ query: 'NR4A2;SNCA;aspirin', perfectSearch: true });
+
+window.minerva.map.triggerSearch({ query: 'NR4A2;SNCA;aspirin;morphine;PINK1' });
+
+window.minerva.map.triggerSearch({ query: 'PINK1', fitBounds: true });
+```
+
+**Search by coordinates**:
+If we want to search using coordinates, we need to provide an object with the following properties as an argument:
+
+- coordinates: this property should indicate the x and y coordinates on the map. Its value should be an object type with x and y properties
+- modelId: this property should indicate submap identifier. Its value should be a number type
+- zoom: this property should indicate zoom level at which we want to trigger search. Its value should be a number type
+- fitBounds: should the map be resized to the rectangle fitting all results. Its value should be a boolean type. This property is optional, and by default, its value is `false`.
+
+##### Example of search by query:
+
+```javascript
+window.minerva.map.triggerSearch({ coordinates: { x: 947, y: 503 }, modelId: 60 });
+
+window.minerva.map.triggerSearch({
+  coordinates: { x: 1947, y: 5203 },
+  modelId: 52,
+  fitBounds: true,
+});
+
+window.minerva.map.triggerSearch({ coordinates: { x: 1947, y: 5203 }, modelId: 60, zoom: 5 });
+
+window.minerva.map.triggerSearch({
+  coordinates: { x: 1947, y: 5203 },
+  modelId: 51,
+  fitBounds: true,
+  zoom: 6,
+});
+```
diff --git a/index.d.ts b/index.d.ts
index dcca7b123d13f373326a60c0142682cc3a4739b2..5cb2d025c040a955b18519285e05e581a97f849b 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -1,3 +1,4 @@
+import { triggerSearch } from '@/services/pluginsManager/map/triggerSearch';
 import { MinervaConfiguration } from '@/services/pluginsManager/pluginsManager';
 
 type Plugin = {
@@ -25,6 +26,9 @@ declare global {
       data: {
         bioEntities: BioEntitiesMethods;
       };
+      map: {
+        triggerSearch: typeof triggerSearch;
+      };
     };
   }
 }
diff --git a/src/components/FunctionalArea/MapNavigation/MapNavigation.component.test.tsx b/src/components/FunctionalArea/MapNavigation/MapNavigation.component.test.tsx
index f6a1cbfe164bc1eeda880d4490e52ff858c30d0c..d944235a29d04b7a1c376869c37cdc29e502ed2d 100644
--- a/src/components/FunctionalArea/MapNavigation/MapNavigation.component.test.tsx
+++ b/src/components/FunctionalArea/MapNavigation/MapNavigation.component.test.tsx
@@ -6,6 +6,7 @@ import {
 import { StoreType } from '@/redux/store';
 import { initialMapDataFixture, openedMapsThreeSubmapsFixture } from '@/redux/map/map.fixtures';
 import { act, render, screen, within } from '@testing-library/react';
+import { MODELS_MOCK } from '@/redux/compartmentPathways/compartmentPathways.mock';
 import { MODELS_DATA_MOCK_WITH_MAIN_MAP } from '@/redux/models/models.mock';
 import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus';
 import { MapNavigation } from './MapNavigation.component';
@@ -104,9 +105,8 @@ describe('MapNavigation - component', () => {
     expect(modelId).toBe(MAIN_MAP_ID);
   });
 
-  it('should close map and open main map if closed currently selected map', async () => {
+  it('should close currently selected map map and open main map', async () => {
     const { store } = renderComponent({
-      models: MODELS_DATA_MOCK_WITH_MAIN_MAP,
       map: {
         data: {
           ...initialMapDataFixture,
@@ -116,6 +116,11 @@ describe('MapNavigation - component', () => {
         loading: 'succeeded',
         error: { message: '', name: '' },
       },
+      models: {
+        loading: 'succeeded',
+        error: { message: '', name: '' },
+        data: MODELS_MOCK,
+      },
     });
 
     const histamineMapButton = screen.getByRole('button', { name: 'Histamine signaling' });
diff --git a/src/components/FunctionalArea/MapNavigation/MapNavigation.component.tsx b/src/components/FunctionalArea/MapNavigation/MapNavigation.component.tsx
index bab3d90aede6a9df3860d67ab25c4a10c546d8d7..c1d4d1206263c5392b481c06f990f482b9f62b16 100644
--- a/src/components/FunctionalArea/MapNavigation/MapNavigation.component.tsx
+++ b/src/components/FunctionalArea/MapNavigation/MapNavigation.component.tsx
@@ -23,7 +23,12 @@ export const MapNavigation = (): JSX.Element => {
   const onCloseSubmap = (event: MouseEvent<HTMLDivElement>, map: OppenedMap): void => {
     event.stopPropagation();
     if (isActive(map.modelId)) {
-      dispatch(closeMapAndSetMainMapActive({ modelId: map.modelId }));
+      dispatch(
+        closeMapAndSetMainMapActive({
+          modelId: mainMapModel.idObject,
+          currentModelId: map.modelId,
+        }),
+      );
 
       PluginsEventBus.dispatchEvent('onSubmapClose', map.modelId);
       PluginsEventBus.dispatchEvent('onSubmapOpen', mainMapModel.idObject);
diff --git a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/PublicationsTable.component.tsx b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/PublicationsTable.component.tsx
index 08cf5805195585f6902bdd2ba02bada9c3dabf42..e71b3bf2a8cb272c0e0f61afc619006120da336c 100644
--- a/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/PublicationsTable.component.tsx
+++ b/src/components/FunctionalArea/Modal/PublicationsModal/PublicationsTable/PublicationsTable.component.tsx
@@ -20,7 +20,7 @@ import {
   useReactTable,
   OnChangeFn,
 } from '@tanstack/react-table';
-import { useState } from 'react';
+import { useRef, useState } from 'react';
 import { SortByHeader } from './SortByHeader';
 import { DEFAULT_PAGE_SIZE } from './PublicationsTable.constants';
 import { FilterBySubmapHeader } from './FilterBySubmapHeader/FilterBySubmapHeader.component';
@@ -88,6 +88,7 @@ export const PublicationsTable = ({ data }: PublicationsTableProps): JSX.Element
 
   const reduxPagination = useAppSelector(paginationSelector);
   const [pagination, setPagination] = useState(reduxPagination);
+  const tableRef = useRef<HTMLTableElement>(null);
 
   const onPaginationChange: OnChangeFn<PaginationState> = updater => {
     /** updating state this way is forced by table library */
@@ -106,8 +107,13 @@ export const PublicationsTable = ({ data }: PublicationsTableProps): JSX.Element
         },
         modelId: selectedId,
       }),
-    );
-    setPagination(nextState);
+    )
+      .unwrap()
+      .then(() => {
+        setPagination(nextState);
+
+        tableRef.current?.scrollIntoView();
+      });
   };
 
   const table = useReactTable({
@@ -126,7 +132,7 @@ export const PublicationsTable = ({ data }: PublicationsTableProps): JSX.Element
   return (
     <div className="flex max-h-full w-full flex-col items-center justify-center bg-white p-6">
       <div className="w-full overflow-auto">
-        <table className="w-full min-w-[1184px] table-auto overflow-auto text-sm">
+        <table className="w-full min-w-[1184px] table-auto overflow-auto text-sm" ref={tableRef}>
           <thead className="sticky top-0 bg-white-pearl">
             {table.getHeaderGroups().map(headerGroup => (
               <tr key={headerGroup.id} className="border-y ">
diff --git a/src/components/Map/Drawer/ProjectInfoDrawer/ProjectInfoDrawer.component.tsx b/src/components/Map/Drawer/ProjectInfoDrawer/ProjectInfoDrawer.component.tsx
index a006e829e5515fb7eb53d8bac0b397187ed4f2e7..211828b2599c579bccaeec9898495a92bdd8ad61 100644
--- a/src/components/Map/Drawer/ProjectInfoDrawer/ProjectInfoDrawer.component.tsx
+++ b/src/components/Map/Drawer/ProjectInfoDrawer/ProjectInfoDrawer.component.tsx
@@ -50,7 +50,7 @@ export const ProjectInfoDrawer = (): JSX.Element => {
         <ul className="list-disc pl-6 ">
           <li className="mt-2 text-hyperlink-blue">
             <button type="button" onClick={onPublicationsClick} className="text-sm font-semibold">
-              (21) publications
+              Publications
             </button>
           </li>
           <li className="mt-2 text-hyperlink-blue">
diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/BioEntitiesResultsList/BioEntitiesPinsList/BioEntitiesPinsList.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/BioEntitiesResultsList/BioEntitiesPinsList/BioEntitiesPinsList.component.tsx
index b563e3f9b9e784e735317cc0ad3eb3f03c40dec0..1a45d10890ed519bf5bc9a3e65744643a1888431 100644
--- a/src/components/Map/Drawer/SearchDrawerWrapper/BioEntitiesResultsList/BioEntitiesPinsList/BioEntitiesPinsList.component.tsx
+++ b/src/components/Map/Drawer/SearchDrawerWrapper/BioEntitiesResultsList/BioEntitiesPinsList/BioEntitiesPinsList.component.tsx
@@ -7,7 +7,7 @@ interface BioEntitiesPinsListProps {
 
 export const BioEntitiesPinsList = ({ bioEnititesPins }: BioEntitiesPinsListProps): JSX.Element => {
   return (
-    <ul className="h-[calc(100vh-198px)] overflow-auto px-6 py-2">
+    <ul className="h-[calc(100%-224px)] max-h-[calc(100%-224px)] overflow-auto px-6 py-2">
       {bioEnititesPins &&
         bioEnititesPins.map(result => (
           <BioEntitiesPinsListItem
diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/BioEntitiesResultsList/BioEntitiesResultsList.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/BioEntitiesResultsList/BioEntitiesResultsList.component.tsx
index eea837b23ac9548a2d50b73feaff0a78ab63d958..1b3b839c7d8218cd0e72571ab671c8fe5805078e 100644
--- a/src/components/Map/Drawer/SearchDrawerWrapper/BioEntitiesResultsList/BioEntitiesResultsList.component.tsx
+++ b/src/components/Map/Drawer/SearchDrawerWrapper/BioEntitiesResultsList/BioEntitiesResultsList.component.tsx
@@ -16,7 +16,7 @@ export const BioEntitiesResultsList = (): JSX.Element => {
   };
 
   return (
-    <div>
+    <div className="h-full">
       <DrawerHeadingBackwardButton backwardFunction={navigateToGroupedSearchResults}>
         {mapName}
       </DrawerHeadingBackwardButton>
diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/GroupedSearchResults.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/GroupedSearchResults.component.tsx
index eef4ed38a8ad4996169ab725701bd98810587017..5d83a4e57d6e8c99daa314a800653e521a41927c 100644
--- a/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/GroupedSearchResults.component.tsx
+++ b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/GroupedSearchResults.component.tsx
@@ -7,7 +7,10 @@ export const CLOSE_BUTTON_ROLE = 'close-drawer-button';
 
 export const GroupedSearchResults = (): JSX.Element => {
   return (
-    <div className="flex flex-col" data-testid="grouped-search-results">
+    <div
+      className="flex h-[calc(100%-124px)] max-h-[calc(100%-124px)] flex-col overflow-auto"
+      data-testid="grouped-search-results"
+    >
       <div className="px-6">
         <Accordion allowZeroExpanded>
           <BioEntitiesAccordion />
diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.component.tsx
index 1ab513f990ea5738852006d056830ca135f7a7b2..6e91ddbfc171362a1fece6eb2ac85673645c519d 100644
--- a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.component.tsx
+++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.component.tsx
@@ -12,7 +12,7 @@ export const PinsList = ({ pinsList, type }: PinsListProps): JSX.Element => {
   switch (type) {
     case 'drugs':
       return (
-        <div className="h-[calc(100vh-198px)] overflow-auto">
+        <div className="h-[calc(100%-214px)] max-h-[calc(100%-214px)] overflow-auto">
           <AccordionsDetails pinsList={pinsList} type={type} />
           <ul className="px-6 py-2" data-testid="pins-list">
             {pinsList.map(result => {
@@ -27,7 +27,7 @@ export const PinsList = ({ pinsList, type }: PinsListProps): JSX.Element => {
       return <div />;
     case 'chemicals':
       return (
-        <div className="h-[calc(100vh-198px)] overflow-auto">
+        <div className="h-[calc(100%-214px)] max-h-[calc(100%-214px)] overflow-auto">
           <AccordionsDetails pinsList={pinsList} type={type} />
           <ul className="px-6 py-2" data-testid="pins-list">
             {pinsList.map(result => {
diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/ResultsList.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/ResultsList.component.tsx
index 0d5ab7ed03ef93748ebe2b19702337d69c097d2a..28f99dd6d6019c02fcda5dc4885eaa601770b3a5 100644
--- a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/ResultsList.component.tsx
+++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/ResultsList.component.tsx
@@ -15,7 +15,7 @@ export const ResultsList = (): JSX.Element => {
   };
 
   return (
-    <div>
+    <div className="h-full">
       <DrawerHeadingBackwardButton backwardFunction={navigateToGroupedSearchResults}>
         <span className="capitalize" data-testid="drawer-heading-text">
           {stepType}
diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/SearchDrawerWrapper.component.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/SearchDrawerWrapper.component.tsx
index 77f0275ef06565495a0983434e306ef6851f9e5f..573435d06b78f5c47a9028078886e65112eadd67 100644
--- a/src/components/Map/Drawer/SearchDrawerWrapper/SearchDrawerWrapper.component.tsx
+++ b/src/components/Map/Drawer/SearchDrawerWrapper/SearchDrawerWrapper.component.tsx
@@ -22,18 +22,18 @@ export const SearchDrawerWrapper = (): JSX.Element => {
     <>
       <SearchDrawerHeader />
       <SearchDrawerTabs />
-      <div data-testid="search-drawer-content">
+      <div data-testid="search-drawer-content" className="h-full max-h-full">
         {/* first step for displaying search results, drawers etc */}
         {currentStep === STEP.FIRST && <GroupedSearchResults />}
         {/* 2nd step for bioEntities aka content */}
         {currentStep === STEP.SECOND && isBioEntityType && (
-          <div data-testid="search-second-step">
+          <div data-testid="search-second-step" className="h-full">
             <BioEntitiesResultsList />
           </div>
         )}
         {/* 2nd step for drugs,chemicals */}
         {currentStep === STEP.SECOND && isChemicalsOrDrugsType && (
-          <div data-testid="search-second-step">
+          <div data-testid="search-second-step" className="h-full">
             <ResultsList />
           </div>
         )}
diff --git a/src/redux/map/map.reducers.ts b/src/redux/map/map.reducers.ts
index de06329e34159742ad61042ffbcb0c658542fc83..362745fc00327c7fff640f6d74c388b85400bb07 100644
--- a/src/redux/map/map.reducers.ts
+++ b/src/redux/map/map.reducers.ts
@@ -1,9 +1,7 @@
-import { ZERO } from '@/constants/common';
 import { DEFAULT_ZOOM } from '@/constants/map';
 import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
 import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus';
 import { getPointMerged } from '../../utils/object/getPointMerged';
-import { MAIN_MAP } from './map.constants';
 import {
   initMapBackground,
   initMapPosition,
@@ -12,6 +10,7 @@ import {
 } from './map.thunks';
 import {
   CloseMapAction,
+  CloseMapActionAndSetMainMapActive,
   MapState,
   OpenMapAndSetActiveAction,
   SetActiveMapAction,
@@ -99,13 +98,12 @@ export const closeMapReducer = (state: MapState, action: CloseMapAction): void =
 
 export const closeMapAndSetMainMapActiveReducer = (
   state: MapState,
-  action: CloseMapAction,
+  action: CloseMapActionAndSetMainMapActive,
 ): void => {
   state.openedMaps = state.openedMaps.filter(
-    openedMap => openedMap.modelId !== action.payload.modelId,
+    openedMap => openedMap.modelId !== action.payload.currentModelId,
   );
-  state.data.modelId =
-    state.openedMaps.find(openedMap => openedMap.modelName === MAIN_MAP)?.modelId || ZERO;
+  state.data.modelId = action.payload.modelId;
 };
 
 export const setMapBackgroundReducer = (state: MapState, action: SetBackgroundAction): void => {
diff --git a/src/redux/map/map.types.ts b/src/redux/map/map.types.ts
index 3d15719aa783b34b5796321c166e30c02a2d9eb6..b11c5cfefe794fb850de5629934e181c4f94877d 100644
--- a/src/redux/map/map.types.ts
+++ b/src/redux/map/map.types.ts
@@ -64,6 +64,12 @@ export type CloseMapActionPayload = Pick<OppenedMap, 'modelId'>;
 
 export type CloseMapAction = PayloadAction<CloseMapActionPayload>;
 
+export type CloseMapActionAndSetMainMapActive = PayloadAction<
+  {
+    currentModelId: number;
+  } & Pick<OppenedMap, 'modelId'>
+>;
+
 export type InitMapDataActionParams = { queryData: QueryData };
 
 export type InitMapDataAction = PayloadAction<SetMapDataAction>;
diff --git a/src/services/pluginsManager/map/triggerSearch/displaySearchDrawerWithSelectedDefaultTab.ts b/src/services/pluginsManager/map/triggerSearch/displaySearchDrawerWithSelectedDefaultTab.ts
new file mode 100644
index 0000000000000000000000000000000000000000..135025d1ddafca34db9642d2d33d863d74c0e5c6
--- /dev/null
+++ b/src/services/pluginsManager/map/triggerSearch/displaySearchDrawerWithSelectedDefaultTab.ts
@@ -0,0 +1,16 @@
+import { getDefaultSearchTab } from '@/components/FunctionalArea/TopBar/SearchBar/SearchBar.utils';
+import { isDrawerOpenSelector } from '@/redux/drawer/drawer.selectors';
+import { openSearchDrawerWithSelectedTab, selectTab } from '@/redux/drawer/drawer.slice';
+import { store } from '@/redux/store';
+
+export const displaySearchDrawerWithSelectedDefaultTab = (searchValues: string[]): void => {
+  const { dispatch, getState } = store;
+  const isDrawerOpen = isDrawerOpenSelector(getState());
+  const defaultSearchTab = getDefaultSearchTab(searchValues);
+
+  if (!isDrawerOpen) {
+    dispatch(openSearchDrawerWithSelectedTab(defaultSearchTab));
+  } else {
+    dispatch(selectTab(defaultSearchTab));
+  }
+};
diff --git a/src/services/pluginsManager/map/triggerSearch/index.ts b/src/services/pluginsManager/map/triggerSearch/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e33cfdc81a3dd7b8d8d8aaf5ec89614628b480c7
--- /dev/null
+++ b/src/services/pluginsManager/map/triggerSearch/index.ts
@@ -0,0 +1 @@
+export { triggerSearch } from './triggerSearch';
diff --git a/src/services/pluginsManager/map/triggerSearch/searchByCoordinates.ts b/src/services/pluginsManager/map/triggerSearch/searchByCoordinates.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bdd6a539ddbfc7eca3934a8700061acc54d8de24
--- /dev/null
+++ b/src/services/pluginsManager/map/triggerSearch/searchByCoordinates.ts
@@ -0,0 +1,27 @@
+import { handleDataReset } from '@/components/Map/MapViewer/utils/listeners/mapSingleClick/handleDataReset';
+import { handleSearchResultAction } from '@/components/Map/MapViewer/utils/listeners/mapSingleClick/handleSearchResultAction';
+import { SIZE_OF_EMPTY_ARRAY } from '@/constants/common';
+import { store } from '@/redux/store';
+import { getElementsByPoint } from '@/utils/search/getElementsByCoordinates';
+import { Coordinates } from './triggerSearch.types';
+
+export const searchByCoordinates = async (
+  coordinates: Coordinates,
+  modelId: number,
+): Promise<void> => {
+  const { dispatch } = store;
+  // side-effect below is to prevent complications with data update - old data may conflict with new data
+  // so we need to reset all the data before updating
+  dispatch(handleDataReset);
+
+  const searchResults = await getElementsByPoint({
+    point: coordinates,
+    currentModelId: modelId,
+  });
+
+  if (!searchResults || searchResults?.length === SIZE_OF_EMPTY_ARRAY) {
+    return;
+  }
+
+  handleSearchResultAction({ searchResults, dispatch });
+};
diff --git a/src/services/pluginsManager/map/triggerSearch/searchByQuery.ts b/src/services/pluginsManager/map/triggerSearch/searchByQuery.ts
new file mode 100644
index 0000000000000000000000000000000000000000..01d0603743171cebd905be553cf7d61aa6824fc1
--- /dev/null
+++ b/src/services/pluginsManager/map/triggerSearch/searchByQuery.ts
@@ -0,0 +1,14 @@
+import { getSearchValuesArrayAndTrimToSeven } from '@/components/FunctionalArea/TopBar/SearchBar/SearchBar.utils';
+import { getSearchData } from '@/redux/search/search.thunks';
+import { store } from '@/redux/store';
+import { displaySearchDrawerWithSelectedDefaultTab } from './displaySearchDrawerWithSelectedDefaultTab';
+
+export const searchByQuery = (query: string, perfectSearch: boolean | undefined): void => {
+  const { dispatch } = store;
+  const searchValues = getSearchValuesArrayAndTrimToSeven(query);
+  const isPerfectMatch = !!perfectSearch;
+
+  dispatch(getSearchData({ searchQueries: searchValues, isPerfectMatch }));
+
+  displaySearchDrawerWithSelectedDefaultTab(searchValues);
+};
diff --git a/src/services/pluginsManager/map/triggerSearch/triggerSearch.test.ts b/src/services/pluginsManager/map/triggerSearch/triggerSearch.test.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3c7408ebf99514fc11abc6bd75118bae26192724
--- /dev/null
+++ b/src/services/pluginsManager/map/triggerSearch/triggerSearch.test.ts
@@ -0,0 +1,205 @@
+/* eslint-disable no-magic-numbers */
+import { mockNetworkNewAPIResponse, mockNetworkResponse } from '@/utils/mockNetworkResponse';
+import { apiPath } from '@/redux/apiPath';
+import { HttpStatusCode } from 'axios';
+import { drugsFixture } from '@/models/fixtures/drugFixtures';
+import { chemicalsFixture } from '@/models/fixtures/chemicalsFixture';
+import { RootState, store } from '@/redux/store';
+import { ELEMENT_SEARCH_RESULT_MOCK_ALIAS } from '@/models/mocks/elementSearchResultMock';
+import { bioEntityResponseFixture } from '@/models/fixtures/bioEntityContentsFixture';
+import { waitFor } from '@testing-library/react';
+import { handleSearchResultAction } from '@/components/Map/MapViewer/utils/listeners/mapSingleClick/handleSearchResultAction';
+import { triggerSearch } from './triggerSearch';
+
+const mockedAxiosClient = mockNetworkNewAPIResponse();
+const mockedAxiosOldClient = mockNetworkResponse();
+const SEARCH_QUERY = 'park7';
+const point = { x: 545.8013, y: 500.9926 };
+const modelId = 1000;
+
+jest.mock('../../../../redux/store');
+jest.mock(
+  '../../../../components/Map/MapViewer/utils/listeners/mapSingleClick/handleSearchResultAction',
+);
+
+describe('triggerSearch', () => {
+  beforeEach(() => {
+    jest.clearAllMocks();
+  });
+  describe('search by query', () => {
+    it('should throw error if query param is wrong type', async () => {
+      const invalidParams = {
+        // eslint-disable-next-line @typescript-eslint/no-explicit-any
+        query: 123 as any,
+      };
+
+      await expect(triggerSearch(invalidParams)).rejects.toThrowError(
+        'Invalid query type. The query should be of string type',
+      );
+    });
+    it('should search for provided query and open drawer when it is not open', async () => {
+      const getState = jest.spyOn(store, 'getState').mockImplementation(
+        () =>
+          ({
+            drawer: {
+              isOpen: false,
+            },
+          }) as RootState,
+      );
+      mockedAxiosClient
+        .onGet(
+          apiPath.getBioEntityContentsStringWithQuery({
+            searchQuery: SEARCH_QUERY,
+            isPerfectMatch: false,
+          }),
+        )
+        .reply(HttpStatusCode.Ok, bioEntityResponseFixture);
+
+      mockedAxiosClient
+        .onGet(apiPath.getDrugsStringWithQuery(SEARCH_QUERY))
+        .reply(HttpStatusCode.Ok, drugsFixture);
+
+      mockedAxiosClient
+        .onGet(apiPath.getChemicalsStringWithQuery(SEARCH_QUERY))
+        .reply(HttpStatusCode.Ok, chemicalsFixture);
+
+      await expect(
+        triggerSearch({
+          query: SEARCH_QUERY,
+        }),
+      ).resolves.toBe(undefined);
+
+      expect(store.dispatch).toHaveBeenCalledTimes(2);
+
+      expect(store.dispatch).not.toHaveBeenCalledWith({
+        payload: SEARCH_QUERY,
+        type: 'drawer/selectTab',
+      });
+
+      expect(store.dispatch).toHaveBeenLastCalledWith({
+        payload: SEARCH_QUERY,
+        type: 'drawer/openSearchDrawerWithSelectedTab',
+      });
+      getState.mockRestore();
+    });
+    it('should search for provided query and select default tab when drawer is already open', async () => {
+      const getState = jest.spyOn(store, 'getState').mockImplementation(
+        () =>
+          ({
+            drawer: {
+              isOpen: true,
+            },
+          }) as RootState,
+      );
+
+      mockedAxiosClient
+        .onGet(
+          apiPath.getBioEntityContentsStringWithQuery({
+            searchQuery: SEARCH_QUERY,
+            isPerfectMatch: false,
+          }),
+        )
+        .reply(HttpStatusCode.Ok, bioEntityResponseFixture);
+
+      mockedAxiosClient
+        .onGet(apiPath.getDrugsStringWithQuery(SEARCH_QUERY))
+        .reply(HttpStatusCode.Ok, drugsFixture);
+
+      mockedAxiosClient
+        .onGet(apiPath.getChemicalsStringWithQuery(SEARCH_QUERY))
+        .reply(HttpStatusCode.Ok, chemicalsFixture);
+
+      await expect(
+        triggerSearch({
+          query: SEARCH_QUERY,
+        }),
+      ).resolves.toBe(undefined);
+
+      expect(getState).toHaveBeenCalled();
+      expect(store.dispatch).toHaveBeenCalledTimes(2);
+      expect(store.dispatch).not.toHaveBeenLastCalledWith({
+        payload: SEARCH_QUERY,
+        type: 'drawer/openSearchDrawerWithSelectedTab',
+      });
+      expect(store.dispatch).toHaveBeenLastCalledWith({
+        payload: SEARCH_QUERY,
+        type: 'drawer/selectTab',
+      });
+
+      getState.mockRestore();
+    });
+  });
+  describe('search by coordinations', () => {
+    it('should throw error if coordinates param is wrong type', async () => {
+      const invalidParams = {
+        // eslint-disable-next-line @typescript-eslint/no-explicit-any
+        coordinates: {} as any,
+        modelId: 53,
+      };
+
+      await expect(triggerSearch(invalidParams)).rejects.toThrowError(
+        'Invalid coordinates type or values',
+      );
+    });
+    it('should throw error if model id param is wrong type', async () => {
+      const invalidParams = {
+        coordinates: { x: 992, y: 993 },
+        // eslint-disable-next-line @typescript-eslint/no-explicit-any
+        modelId: '53' as any,
+      };
+
+      await expect(triggerSearch(invalidParams)).rejects.toThrowError(
+        'Invalid model id type. The model should be of number type',
+      );
+    });
+    it('should search result with proper data', async () => {
+      mockedAxiosOldClient
+        .onGet(apiPath.getSingleBioEntityContentsStringWithCoordinates(point, modelId))
+        .reply(HttpStatusCode.Ok, [ELEMENT_SEARCH_RESULT_MOCK_ALIAS]);
+
+      mockedAxiosClient
+        .onGet(
+          apiPath.getBioEntityContentsStringWithQuery({
+            searchQuery: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id.toString(),
+            isPerfectMatch: true,
+          }),
+        )
+        .reply(HttpStatusCode.Ok, bioEntityResponseFixture);
+      const params = {
+        coordinates: point,
+        modelId,
+      };
+
+      await expect(triggerSearch(params)).resolves.toBe(undefined);
+
+      await waitFor(() => {
+        expect(handleSearchResultAction).toHaveBeenCalledWith({
+          searchResults: [ELEMENT_SEARCH_RESULT_MOCK_ALIAS],
+          dispatch: store.dispatch,
+        });
+      });
+    });
+    it('should not search result if there is no bio entity with specific coordinates', async () => {
+      mockedAxiosOldClient
+        .onGet(apiPath.getSingleBioEntityContentsStringWithCoordinates(point, modelId))
+        .reply(HttpStatusCode.Ok, []);
+
+      mockedAxiosClient
+        .onGet(
+          apiPath.getBioEntityContentsStringWithQuery({
+            searchQuery: ELEMENT_SEARCH_RESULT_MOCK_ALIAS.id.toString(),
+            isPerfectMatch: true,
+          }),
+        )
+        .reply(HttpStatusCode.Ok, bioEntityResponseFixture);
+      const params = {
+        coordinates: point,
+        modelId,
+      };
+
+      await expect(triggerSearch(params)).resolves.toBe(undefined);
+
+      expect(handleSearchResultAction).not.toHaveBeenCalled();
+    });
+  });
+});
diff --git a/src/services/pluginsManager/map/triggerSearch/triggerSearch.ts b/src/services/pluginsManager/map/triggerSearch/triggerSearch.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4a747095a9d1fdf446b603ea06e0760b40c9a906
--- /dev/null
+++ b/src/services/pluginsManager/map/triggerSearch/triggerSearch.ts
@@ -0,0 +1,28 @@
+import { SearchParams } from './triggerSearch.types';
+import { searchByQuery } from './searchByQuery';
+import { searchByCoordinates } from './searchByCoordinates';
+
+export async function triggerSearch(params: SearchParams): Promise<void> {
+  if ('query' in params) {
+    if (typeof params.query !== 'string') {
+      throw new Error('Invalid query type. The query should be of string type');
+    }
+    searchByQuery(params.query, params.perfectSearch);
+  } else {
+    const areCoordinatesInvalidType =
+      typeof params.coordinates !== 'object' || params.coordinates === null;
+    const areCoordinatesMissingKeys = !('x' in params.coordinates) || !('y' in params.coordinates);
+    const areCoordinatesValuesInvalid =
+      typeof params.coordinates.x !== 'number' || typeof params.coordinates.y !== 'number';
+
+    if (areCoordinatesInvalidType || areCoordinatesMissingKeys || areCoordinatesValuesInvalid) {
+      throw new Error('Invalid coordinates type or values');
+    }
+
+    if (typeof params.modelId !== 'number') {
+      throw new Error('Invalid model id type. The model should be of number type');
+    }
+
+    searchByCoordinates(params.coordinates, params.modelId);
+  }
+}
diff --git a/src/services/pluginsManager/map/triggerSearch/triggerSearch.types.ts b/src/services/pluginsManager/map/triggerSearch/triggerSearch.types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fa6cd67ad8cc1ad8db58bc7ca2cf7ddd15dba6ab
--- /dev/null
+++ b/src/services/pluginsManager/map/triggerSearch/triggerSearch.types.ts
@@ -0,0 +1,19 @@
+export type SearchByQueryParams = {
+  query: string;
+  perfectSearch?: boolean;
+  fitBounds?: boolean;
+};
+
+export type Coordinates = {
+  x: number;
+  y: number;
+};
+
+export type SearchByCoordinatesParams = {
+  coordinates: Coordinates;
+  modelId: number;
+  fitBounds?: boolean;
+  zoom?: number;
+};
+
+export type SearchParams = SearchByCoordinatesParams | SearchByQueryParams;
diff --git a/src/services/pluginsManager/pluginsManager.ts b/src/services/pluginsManager/pluginsManager.ts
index f8de68422b7484c961e37996570a277287105ffe..a38faee9ce26d2a85034ab8e24c9e697827b0d74 100644
--- a/src/services/pluginsManager/pluginsManager.ts
+++ b/src/services/pluginsManager/pluginsManager.ts
@@ -3,9 +3,10 @@ import { registerPlugin } from '@/redux/plugins/plugins.thunks';
 import { store } from '@/redux/store';
 import md5 from 'crypto-js/md5';
 import { bioEntitiesMethods } from './bioEntities';
+import { triggerSearch } from './map/triggerSearch';
+import { PluginsEventBus } from './pluginsEventBus';
 import type { PluginsManagerType } from './pluginsManager.types';
 import { configurationMapper } from './pluginsManager.utils';
-import { PluginsEventBus } from './pluginsEventBus';
 
 export const PluginsManager: PluginsManagerType = {
   hashedPlugins: {},
@@ -24,6 +25,9 @@ export const PluginsManager: PluginsManagerType = {
       data: {
         bioEntities: bioEntitiesMethods,
       },
+      map: {
+        triggerSearch,
+      },
     };
 
     const unsubscribe = store.subscribe(() => {