diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Others/MapWidget_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Map/MapWidget_Spec.ts
similarity index 100%
rename from app/client/cypress/e2e/Regression/ClientSide/Widgets/Others/MapWidget_Spec.ts
rename to app/client/cypress/e2e/Regression/ClientSide/Widgets/Map/MapWidget_Spec.ts
diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Others/MapWidget_loading_Spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Map/MapWidget_loading_Spec.ts
similarity index 100%
rename from app/client/cypress/e2e/Regression/ClientSide/Widgets/Others/MapWidget_loading_Spec.ts
rename to app/client/cypress/e2e/Regression/ClientSide/Widgets/Map/MapWidget_loading_Spec.ts
diff --git a/app/client/src/widgets/MapWidget/component/MapComponent.test.tsx b/app/client/src/widgets/MapWidget/component/MapComponent.test.tsx
new file mode 100644
index 0000000000..998a4d382d
--- /dev/null
+++ b/app/client/src/widgets/MapWidget/component/MapComponent.test.tsx
@@ -0,0 +1,121 @@
+import React from "react";
+import { render } from "@testing-library/react";
+import Marker from "../component/Marker";
+
+// Mock the google maps API
+const mockAddListener = jest.fn().mockImplementation((event, callback) => {
+ // Store the callback to simulate click events
+ if (event === "click") {
+ (
+ mockAddListener as unknown as {
+ clickCallback: (...args: unknown[]) => void;
+ }
+ ).clickCallback = callback;
+ }
+
+ return "listener-id"; // Return a mock listener ID
+});
+
+const mockRemoveListener = jest.fn();
+const mockSetMap = jest.fn();
+const mockSetIcon = jest.fn();
+const mockSetPosition = jest.fn();
+const mockSetTitle = jest.fn();
+
+// Add type declaration for the global google object
+declare global {
+ interface Window {
+ google: unknown;
+ }
+}
+
+// Mock the google object
+(global as unknown as { google: unknown }).google = {
+ maps: {
+ Marker: jest.fn().mockImplementation(() => ({
+ setMap: mockSetMap,
+ setIcon: mockSetIcon,
+ setPosition: mockSetPosition,
+ setTitle: mockSetTitle,
+ addListener: mockAddListener,
+ })),
+ Point: jest.fn().mockImplementation((x, y) => ({ x, y })),
+ event: {
+ clearListeners: jest.fn(),
+ removeListener: mockRemoveListener,
+ },
+ },
+};
+
+describe("Map Widget - Marker Component", () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it("should trigger onClick callback only once per click", () => {
+ const onClickMock = jest.fn();
+
+ // Render the marker component with onClick handler
+ render(
+ ,
+ );
+
+ // Simulate a marker click by directly calling the stored callback
+ (
+ mockAddListener as unknown as {
+ clickCallback: (...args: unknown[]) => void;
+ }
+ ).clickCallback();
+
+ // Verify onClick was called exactly once
+ expect(onClickMock).toHaveBeenCalledTimes(1);
+
+ // Simulate another click
+ (
+ mockAddListener as unknown as {
+ clickCallback: (...args: unknown[]) => void;
+ }
+ ).clickCallback();
+
+ // Verify onClick was called exactly twice (once per click)
+ expect(onClickMock).toHaveBeenCalledTimes(2);
+ });
+
+ it("should clear previous click listeners when onClick prop changes", () => {
+ const { rerender } = render(
+ ,
+ );
+
+ // Verify clearListeners was called during initial render
+ expect(google.maps.event.clearListeners).toHaveBeenCalledWith(
+ expect.anything(),
+ "click",
+ );
+
+ // Reset the mock to check if it's called again
+ (google.maps.event.clearListeners as jest.Mock).mockClear();
+
+ // Rerender with a different onClick handler
+ rerender(
+ ,
+ );
+
+ // Verify clearListeners was called again when onClick changed
+ expect(google.maps.event.clearListeners).toHaveBeenCalledWith(
+ expect.anything(),
+ "click",
+ );
+ });
+});
diff --git a/app/client/src/widgets/MapWidget/component/Marker.tsx b/app/client/src/widgets/MapWidget/component/Marker.tsx
index 7701ded5be..ef9bec51e4 100644
--- a/app/client/src/widgets/MapWidget/component/Marker.tsx
+++ b/app/client/src/widgets/MapWidget/component/Marker.tsx
@@ -31,10 +31,6 @@ const Marker: React.FC = (options) => {
title,
});
- googleMapMarker.addListener("click", () => {
- if (onClick) onClick();
- });
-
setMarker(googleMapMarker);
}
@@ -79,19 +75,32 @@ const Marker: React.FC = (options) => {
useEffect(() => {
if (!marker) return;
- marker.addListener("click", () => {
+ google.maps.event.clearListeners(marker, "click");
+ const clickListener = marker.addListener("click", () => {
if (onClick) onClick();
});
+
+ return () => {
+ google.maps.event.removeListener(clickListener);
+ };
}, [marker, onClick]);
// add dragend event on marker
useEffect(() => {
- if (!marker) return;
+ if (!marker || !onDragEnd) return;
- marker.addListener("dragend", (e: google.maps.MapMouseEvent) => {
- if (onDragEnd) onDragEnd(e);
- });
- }, [marker, options.onDragEnd]);
+ google.maps.event.clearListeners(marker, "dragend");
+ const dragEndListener = marker.addListener(
+ "dragend",
+ (e: google.maps.MapMouseEvent) => {
+ if (onDragEnd) onDragEnd(e);
+ },
+ );
+
+ return () => {
+ google.maps.event.removeListener(dragEndListener);
+ };
+ }, [marker, onDragEnd]);
return null;
};