diff --git a/examples/autoComplete/example.js b/examples/autoComplete/example.js index 38cf90d..d23847c 100644 --- a/examples/autoComplete/example.js +++ b/examples/autoComplete/example.js @@ -13,16 +13,19 @@ let placesService; let predictionItems = []; let markers = []; -function initMap() { +async function initMap() { const austinCoords = { lat: 30.268193, lng: -97.7457518 }; // Austin, TX :) - map = new google.maps.Map(document.getElementById("map"), { + const { Map } = await google.maps.importLibrary("maps"); + map = new Map(document.getElementById("map"), { center: austinCoords, zoom: 11, + mapId: "DEMO_MAP_ID", }); - placesService = new google.maps.places.PlacesService(map); - const autocompleteService = new google.maps.places.AutocompleteService(); + const { AutocompleteService, PlacesService, PlacesServiceStatus } = await google.maps.importLibrary("places"); + placesService = new PlacesService(map); + const autocompleteService = new AutocompleteService(); const searchInput = $("#search-input"); searchInput.autocomplete({ @@ -48,7 +51,7 @@ function initMap() { locationBias: map.getCenter(), }, function (predictions, status) { - if (status != google.maps.places.PlacesServiceStatus.OK || !predictions) { + if (status != PlacesServiceStatus.OK || !predictions) { response([]); return; } @@ -100,13 +103,15 @@ function getPredictionInfo(prediction) { } } -function getPlaceDetails(placeId) { +async function getPlaceDetails(placeId) { + const { PlacesServiceStatus } = await google.maps.importLibrary("places"); + var request = { placeId: placeId, }; placesService.getDetails(request, function (result, status) { - if (status === google.maps.places.PlacesServiceStatus.OK) { + if (status === PlacesServiceStatus.OK) { createMarker(result); map.setCenter(result.geometry.location); map.setZoom(14); @@ -114,16 +119,19 @@ function getPlaceDetails(placeId) { }); } -function getTextSearch(query) { +async function getTextSearch(query) { var request = { query: query, location: map.getCenter(), }; - const resultsBounds = new google.maps.LatLngBounds(); + const { PlacesServiceStatus } = await google.maps.importLibrary("places"); + + const { LatLngBounds } = await google.maps.importLibrary("core"); + const resultsBounds = new LatLngBounds(); placesService.textSearch(request, function (results, status) { - if (status === google.maps.places.PlacesServiceStatus.OK) { + if (status === PlacesServiceStatus.OK) { results.map((result) => { createMarker(result); @@ -137,10 +145,11 @@ function getTextSearch(query) { }); } -function createMarker(place) { +async function createMarker(place) { if (!place.geometry || !place.geometry.location) return; - const marker = new google.maps.Marker({ + const { AdvancedMarkerElement } = await google.maps.importLibrary("marker"); + const marker = new AdvancedMarkerElement({ map, position: place.geometry.location, }); @@ -148,7 +157,9 @@ function createMarker(place) { markers.push(marker); // TODO: Update this example to display an InfoWindow when clicking on the marker to display additional details (from getDetails) - marker.addListener(marker, "click", () => { + marker.addListener("click", () => { console.log("MARKER CLICKED", place.name); }); } + +initMap(); diff --git a/examples/autoComplete/google.template.html b/examples/autoComplete/google.template.html index c017dc2..833ee63 100644 --- a/examples/autoComplete/google.template.html +++ b/examples/autoComplete/google.template.html @@ -9,7 +9,7 @@ - + @@ -19,10 +19,45 @@
- + diff --git a/examples/autoComplete/index.template.html b/examples/autoComplete/index.template.html index cbf3cd5..159ba61 100644 --- a/examples/autoComplete/index.template.html +++ b/examples/autoComplete/index.template.html @@ -9,7 +9,7 @@ - + @@ -19,10 +19,6 @@
- + diff --git a/src/index.ts b/src/index.ts index 58bd2ad..c76c56f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -108,6 +108,57 @@ const migrationInit = async function () { PlacesService: MigrationPlacesService, PlacesServiceStatus: PlacesServiceStatus, }, + + // Handle dynamic imports, e.g. const { Map } = await google.maps.importLibrary("maps"); + importLibrary: (library) => { + return new Promise((resolve) => { + switch (library) { + case "core": + resolve({ + ControlPosition: MigrationControlPosition, + LatLng: MigrationLatLng, + LatLngBounds: MigrationLatLngBounds, + }); + break; + + case "maps": + resolve({ + InfoWindow: MigrationInfoWindow, + Map: MigrationMap, + }); + break; + + case "places": + resolve({ + AutocompleteService: MigrationAutocompleteService, + PlacesService: MigrationPlacesService, + PlacesServiceStatus: PlacesServiceStatus, + }); + break; + + case "routes": + resolve({ + DirectionsRenderer: MigrationDirectionsRenderer, + DirectionsService: MigrationDirectionsService, + DirectionsStatus: DirectionsStatus, + TravelMode: TravelMode, + }); + break; + + case "marker": + resolve({ + AdvancedMarkerElement: MigrationMarker, + Marker: MigrationMarker, + }); + break; + + default: + console.error(`Unsupported library: ${library}`); + resolve({}); + break; + } + }); + }, }, }; diff --git a/test/index.test.ts b/test/index.test.ts index 724dece..c97950f 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -24,6 +24,9 @@ const mockMigrationCallback = jest.fn(); // Import the migration adapter after our mock script HTMLScriptElement has been setup import "../src/index"; +// Spy on console.error so we can verify it gets called in error cases +jest.spyOn(console, "error").mockImplementation(() => {}); + afterEach(() => { jest.clearAllMocks(); }); @@ -55,3 +58,69 @@ test("importing the adapter should populate google.maps namespace for direct loa // Verify our mock callback has been invoked after loading the adapter expect(mockMigrationCallback).toHaveBeenCalledTimes(1); }); + +test("can dynamically import core classes", async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const google = (window as any).google; + + const { ControlPosition, LatLng, LatLngBounds } = await google.maps.importLibrary("core"); + + expect(ControlPosition).toBeDefined(); + expect(LatLng).toBeDefined(); + expect(LatLngBounds).toBeDefined(); +}); + +test("can dynamically import maps classes", async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const google = (window as any).google; + + const { InfoWindow, Map } = await google.maps.importLibrary("maps"); + + expect(InfoWindow).toBeDefined(); + expect(Map).toBeDefined(); +}); + +test("can dynamically import places classes", async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const google = (window as any).google; + + const { AutocompleteService, PlacesService, PlacesServiceStatus } = await google.maps.importLibrary("places"); + + expect(AutocompleteService).toBeDefined(); + expect(PlacesService).toBeDefined(); + expect(PlacesServiceStatus).toBeDefined(); +}); + +test("can dynamically import routes classes", async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const google = (window as any).google; + + const { DirectionsRenderer, DirectionsService, DirectionsStatus, TravelMode } = await google.maps.importLibrary( + "routes", + ); + + expect(DirectionsRenderer).toBeDefined(); + expect(DirectionsService).toBeDefined(); + expect(DirectionsStatus).toBeDefined(); + expect(TravelMode).toBeDefined(); +}); + +test("can dynamically import marker classes", async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const google = (window as any).google; + + const { AdvancedMarkerElement, Marker } = await google.maps.importLibrary("marker"); + + expect(AdvancedMarkerElement).toBeDefined(); + expect(Marker).toBeDefined(); +}); + +test("should report an error if a library we don't support is requested", async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const google = (window as any).google; + + const { ThisClassWontExist } = await google.maps.importLibrary("INVALID_LIBRARY"); + + expect(ThisClassWontExist).toBeUndefined(); + expect(console.error).toHaveBeenCalledTimes(1); +});