From c9586395d7a8168190c4ae1ac761ec18d2bbc5fc Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Tue, 30 Sep 2025 22:40:00 +0200 Subject: [PATCH] [Map][Google] Upgrade @googlemaps/js-api-loader to ^2.0 --- UPGRADE-3.0.md | 6 +++ pnpm-lock.yaml | 31 +++++++++-- src/Map/src/Bridge/Google/CHANGELOG.md | 6 +++ src/Map/src/Bridge/Google/README.md | 24 ++++----- .../Google/assets/dist/map_controller.d.ts | 4 +- .../Google/assets/dist/map_controller.js | 37 +++++-------- src/Map/src/Bridge/Google/assets/package.json | 6 +-- .../Google/assets/src/map_controller.ts | 52 ++++++------------- .../Google/src/Renderer/GoogleRenderer.php | 38 +++++++------- .../src/Renderer/GoogleRendererFactory.php | 12 ++--- .../tests/GoogleRendererFactoryTest.php | 10 ++-- .../Google/tests/GoogleRendererTest.php | 26 +++++----- ...ap with data set markers with icons__1.txt | 2 +- ...et simple map, with minimum options__1.txt | 2 +- ...h data set with all markers removed__1.txt | 2 +- ...ta set with circles and infoWindows__1.txt | 2 +- ...with data set with controls enabled__1.txt | 2 +- ...ith data set with custom attributes__1.txt | 2 +- ...t map id overridden by option mapId__1.txt | 2 +- ... passing options (except the mapId)__1.txt | 2 +- ...p with data set with default map id__1.txt | 2 +- ...ap with data set with every options__1.txt | 2 +- ...p with data set with map extra data__1.txt | 2 +- ...th marker remove and new ones added__1.txt | 2 +- ...ta set with markers and infoWindows__1.txt | 2 +- ...a set with polygons and infoWindows__1.txt | 2 +- ... set with polylines and infoWindows__1.txt | 2 +- ...set with rectangles and infoWindows__1.txt | 2 +- ...h data set without controls enabled__1.txt | 2 +- 29 files changed, 144 insertions(+), 142 deletions(-) diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md index 7ae847d5c02..74a45fccc44 100644 --- a/UPGRADE-3.0.md +++ b/UPGRADE-3.0.md @@ -31,6 +31,12 @@ class MyLiveComponent { * The Twig function `render_map()` has been removed, use `ux_map()` instead * The option `title` from `Polygon`, `Polyline`, `Rectangle` and `Circle`, has been removed, use option `infoWindow` instead * The property `rawOptions` from `ux:map:*:before-create` events has been removed, use `bridgeOptions` instead +* The Google Map Bridge has been upgraded to use `@googlemaps/js-api-loader` version `^2.0.0`: + * If you use Symfony AssetMapper without Symfony Flex, run `bin/console importmap:require @googlemaps/js-api-loader@^2.0` + * Options configurable through `UX_MAP_DSN` query params have changed: + * Option `version` has been renamed to `v` + * Options `authReferrerPolicy`, `mapIds`, `channel`, `solutionChannel` have been added + * Options `ìd`, `nonce`, `retries`, `url` have been removed ## Swup diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6a1cd9243af..06a9deeca02 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -258,8 +258,8 @@ importers: version: link:../../../../assets devDependencies: '@googlemaps/js-api-loader': - specifier: ^1.16.6 - version: 1.16.10 + specifier: ^2.0.0 + version: 2.0.0 '@hotwired/stimulus': specifier: ^3.0.0 version: 3.2.2 @@ -714,24 +714,28 @@ packages: engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] + libc: [musl] '@biomejs/cli-linux-arm64@2.1.2': resolution: {integrity: sha512-NWNy2Diocav61HZiv2enTQykbPP/KrA/baS7JsLSojC7Xxh2nl9IczuvE5UID7+ksRy2e7yH7klm/WkA72G1dw==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] + libc: [glibc] '@biomejs/cli-linux-x64-musl@2.1.2': resolution: {integrity: sha512-xlB3mU14ZUa3wzLtXfmk2IMOGL+S0aHFhSix/nssWS/2XlD27q+S6f0dlQ8WOCbYoXcuz8BCM7rCn2lxdTrlQA==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] + libc: [musl] '@biomejs/cli-linux-x64@2.1.2': resolution: {integrity: sha512-Km/UYeVowygTjpX6sGBzlizjakLoMQkxWbruVZSNE6osuSI63i4uCeIL+6q2AJlD3dxoiBJX70dn1enjQnQqwA==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] + libc: [glibc] '@biomejs/cli-win32-arm64@2.1.2': resolution: {integrity: sha512-G8KWZli5ASOXA3yUQgx+M4pZRv3ND16h77UsdunUL17uYpcL/UC7RkWTdkfvMQvogVsAuz5JUcBDjgZHXxlKoA==} @@ -1091,8 +1095,8 @@ packages: '@formatjs/intl-localematcher@0.6.1': resolution: {integrity: sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg==} - '@googlemaps/js-api-loader@1.16.10': - resolution: {integrity: sha512-c2erv2k7P2ilYzMmtYcMgAR21AULosQuUHJbStnrvRk2dG93k5cqptDrh9A8p+ZNlyhiqEOgHW7N9PAizdUM7Q==} + '@googlemaps/js-api-loader@2.0.0': + resolution: {integrity: sha512-KRE1QA5awHXr+odGOMkoKKYok5vMnpr7s+jU+1Y/v7+M7/2db5InfsfrhOGxnJRRig4L9ga7RuPf7P9fLxg1TA==} '@hotwired/stimulus-webpack-helpers@1.0.1': resolution: {integrity: sha512-wa/zupVG0eWxRYJjC1IiPBdt3Lruv0RqGN+/DTMmUWUyMAEB27KXmVY6a8YpUVTM7QwVuaLNGW4EqDgrS2upXQ==} @@ -1236,56 +1240,67 @@ packages: resolution: {integrity: sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.45.1': resolution: {integrity: sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.45.1': resolution: {integrity: sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.45.1': resolution: {integrity: sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.45.1': resolution: {integrity: sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-powerpc64le-gnu@4.45.1': resolution: {integrity: sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.45.1': resolution: {integrity: sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.45.1': resolution: {integrity: sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.45.1': resolution: {integrity: sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.45.1': resolution: {integrity: sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.45.1': resolution: {integrity: sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.45.1': resolution: {integrity: sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==} @@ -2349,24 +2364,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.30.1: resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.30.1: resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.30.1: resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.30.1: resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} @@ -3694,7 +3713,9 @@ snapshots: dependencies: tslib: 2.8.1 - '@googlemaps/js-api-loader@1.16.10': {} + '@googlemaps/js-api-loader@2.0.0': + dependencies: + '@types/google.maps': 3.58.1 '@hotwired/stimulus-webpack-helpers@1.0.1(@hotwired/stimulus@3.2.2)': dependencies: diff --git a/src/Map/src/Bridge/Google/CHANGELOG.md b/src/Map/src/Bridge/Google/CHANGELOG.md index 09d59834976..ef22daf6ccc 100644 --- a/src/Map/src/Bridge/Google/CHANGELOG.md +++ b/src/Map/src/Bridge/Google/CHANGELOG.md @@ -4,6 +4,12 @@ - Minimum required Symfony version is now 6.4 - Minimum required PHP version is now 8.2 +- Upgrade `@googlemaps/js-api-loader` JavaScript package from `^1.16.6` to `^2.0.0`: + - If you use Symfony AssetMapper without Symfony Flex, run `bin/console importmap:require @googlemaps/js-api-loader@^2.0` + - Options configurable through `UX_MAP_DSN` query params have changed: + - Option `version` has been renamed to `v` + - Options `authReferrerPolicy`, `mapIds`, `channel`, `solutionChannel` have been added + - Options `ìd`, `nonce`, `retries`, `url` have been removed ## 2.31 diff --git a/src/Map/src/Bridge/Google/README.md b/src/Map/src/Bridge/Google/README.md index 9b6e5baa9cf..4ed87722cf7 100644 --- a/src/Map/src/Bridge/Google/README.md +++ b/src/Map/src/Bridge/Google/README.md @@ -26,24 +26,24 @@ npm run watch ```dotenv UX_MAP_DSN=google://GOOGLE_MAPS_API_KEY@default -# With options -UX_MAP_DSN=google://GOOGLE_MAPS_API_KEY@default?version=weekly +# With +UX_MAP_DSN=google://GOOGLE_MAPS_API_KEY@default?v=weekly UX_MAP_DSN=google://GOOGLE_MAPS_API_KEY@default?language=fr®ion=FR UX_MAP_DSN=google://GOOGLE_MAPS_API_KEY@default?libraries[]=geometry&libraries[]=places ``` Available options: -| Option | Description | Default | -|-------------|------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------| -| `id` | The id of the script tag | `__googleMapsScriptId` | -| `language` | Force language, see [list of supported languages](https://developers.google.com/maps/faq#languagesupport) specified in the browser | The user's preferred language | -| `region` | Unicode region subtag identifiers compatible with [ISO 3166-1](https://en.wikipedia.org/wiki/ISO_3166-1) | | -| `nonce` | Use a cryptographic nonce attribute | | -| `retries` | The number of script load retries | 3 | -| `url` | Custom url to load the Google Maps API script | `https://maps.googleapis.com/maps/api/js` | -| `version` | The release channels or version numbers | `weekly` | -| `libraries` | The additional libraries to load, see [list of supported libraries](https://googlemaps.github.io/js-api-loader/types/Library.html) | `['maps', 'marker']`, those two libraries are always loaded | +| Option | Description | Default | +|----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------| +| `v` | The version of the Maps JavaScript API to load, could be a [release channels](https://developers.google.com/maps/documentation/javascript/versions) or version numbers | `weekly` | +| `language` | The language to use, see [list of supported languages](https://developers.google.com/maps/faq#languagesupport) specified in the browser | The user's preferred language | +| `region` | The region code to use, unicode region subtag identifiers compatible with [ISO 3166-1](https://en.wikipedia.org/wiki/ISO_3166-1) | | +| `libraries` | Additional libraries to load, see [list of supported libraries](https://developers.google.com/maps/documentation/javascript/libraries#available-libraries) | `['maps', 'marker']`, those two libraries are always loaded | +| `authReferrerPolicy` | Set the referrer policy for the API requests | | +| `mapIds` | An array of map IDs to preload | | +| `channel` | Can be used to track your usage | | +| `solutionChannel` | Used by the Google Maps Platform to track adoption and usage of examples and solutions | | ## Map options diff --git a/src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts b/src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts index b99e974823b..f2616b19882 100644 --- a/src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts +++ b/src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts @@ -1,9 +1,9 @@ -import { LoaderOptions } from '@googlemaps/js-api-loader'; +import { APIOptions } from '@googlemaps/js-api-loader'; import AbstractMapController, { MapDefinition, MarkerDefinition, PolygonDefinition, PolylineDefinition, CircleDefinition, RectangleDefinition, InfoWindowDefinition, Icon } from '@symfony/ux-map'; type MapOptions = Pick; declare class export_default extends AbstractMapController { - providerOptionsValue: Pick; + providerOptionsValue: Pick; map: google.maps.Map; connect(): Promise; centerValueChanged(): void; diff --git a/src/Map/src/Bridge/Google/assets/dist/map_controller.js b/src/Map/src/Bridge/Google/assets/dist/map_controller.js index 691842216cf..7ced22e8e00 100644 --- a/src/Map/src/Bridge/Google/assets/dist/map_controller.js +++ b/src/Map/src/Bridge/Google/assets/dist/map_controller.js @@ -1,5 +1,5 @@ // src/map_controller.ts -import { Loader } from "@googlemaps/js-api-loader"; +import { importLibrary, setOptions } from "@googlemaps/js-api-loader"; // ../../../../assets/dist/abstract_map_controller.js import { Controller } from "@hotwired/stimulus"; @@ -146,7 +146,6 @@ abstract_map_controller_default.values = { }; // src/map_controller.ts -var _google; var _loading = false; var _loaded = false; var _onLoadedCallbacks = []; @@ -163,22 +162,10 @@ var map_controller_default = class extends abstract_map_controller_default { return; } _loading = true; - _google = { maps: {} }; let { libraries = [], ...loaderOptions } = this.providerOptionsValue; - const loader = new Loader(loaderOptions); + setOptions(loaderOptions); libraries = ["core", ...libraries.filter((library) => library !== "core")]; - const librariesImplementations = await Promise.all(libraries.map((library) => loader.importLibrary(library))); - librariesImplementations.map((libraryImplementation, index) => { - if (typeof libraryImplementation !== "object" || libraryImplementation === null) { - return; - } - const library = libraries[index]; - if (["marker", "places", "geometry", "journeySharing", "drawing", "visualization"].includes(library)) { - _google.maps[library] = libraryImplementation; - } else { - _google.maps = { ..._google.maps, ...libraryImplementation }; - } - }); + await Promise.all(libraries.map((library) => importLibrary(library))); _loading = false; _loaded = true; onLoaded(); @@ -206,7 +193,7 @@ var map_controller_default = class extends abstract_map_controller_default { } } dispatchEvent(name, payload = {}) { - payload.google = _google; + payload.google = google; this.dispatch(name, { prefix: "ux:map", detail: payload @@ -218,7 +205,7 @@ var map_controller_default = class extends abstract_map_controller_default { options.mapTypeControl = typeof options.mapTypeControlOptions !== "undefined"; options.streetViewControl = typeof options.streetViewControlOptions !== "undefined"; options.fullscreenControl = typeof options.fullscreenControlOptions !== "undefined"; - return new _google.maps.Map(this.element, { + return new google.maps.Map(this.element, { center, zoom, minZoom, @@ -231,7 +218,7 @@ var map_controller_default = class extends abstract_map_controller_default { definition }) { const { "@id": _id, position, title, infoWindow, icon, bridgeOptions = {} } = definition; - const marker = new _google.maps.marker.AdvancedMarkerElement({ + const marker = new google.maps.marker.AdvancedMarkerElement({ position, title, map: this.map, @@ -255,7 +242,7 @@ var map_controller_default = class extends abstract_map_controller_default { definition }) { const { "@id": _id, points, infoWindow, bridgeOptions = {} } = definition; - const polygon = new _google.maps.Polygon({ + const polygon = new google.maps.Polygon({ paths: points, map: this.map, ...bridgeOptions @@ -272,7 +259,7 @@ var map_controller_default = class extends abstract_map_controller_default { definition }) { const { "@id": _id, points, infoWindow, bridgeOptions = {} } = definition; - const polyline = new _google.maps.Polyline({ + const polyline = new google.maps.Polyline({ path: points, map: this.map, ...bridgeOptions @@ -287,7 +274,7 @@ var map_controller_default = class extends abstract_map_controller_default { } doCreateCircle({ definition }) { const { "@id": _id, center, radius, infoWindow, bridgeOptions = {} } = definition; - const circle = new _google.maps.Circle({ + const circle = new google.maps.Circle({ center, radius, map: this.map, @@ -305,8 +292,8 @@ var map_controller_default = class extends abstract_map_controller_default { definition }) { const { northEast, southWest, infoWindow, bridgeOptions = {} } = definition; - const rectangle = new _google.maps.Rectangle({ - bounds: new _google.maps.LatLngBounds(southWest, northEast), + const rectangle = new google.maps.Rectangle({ + bounds: new google.maps.LatLngBounds(southWest, northEast), map: this.map, ...bridgeOptions }); @@ -336,7 +323,7 @@ var map_controller_default = class extends abstract_map_controller_default { position, ...bridgeOptions }; - const infoWindow = new _google.maps.InfoWindow(infoWindowOptions); + const infoWindow = new google.maps.InfoWindow(infoWindowOptions); element.addListener("click", (event) => { if (autoClose) { this.closeInfoWindowsExcept(infoWindow); diff --git a/src/Map/src/Bridge/Google/assets/package.json b/src/Map/src/Bridge/Google/assets/package.json index 929ba83fc68..d7d3ccb6045 100644 --- a/src/Map/src/Bridge/Google/assets/package.json +++ b/src/Map/src/Bridge/Google/assets/package.json @@ -36,12 +36,12 @@ }, "importmap": { "@hotwired/stimulus": "^3.0.0", - "@googlemaps/js-api-loader": "^1.16.6", + "@googlemaps/js-api-loader": "^2.0.0", "@symfony/ux-google-map": "path:%PACKAGE%/dist/map_controller.js" } }, "peerDependencies": { - "@googlemaps/js-api-loader": "^1.16.6", + "@googlemaps/js-api-loader": "^2.0.0", "@hotwired/stimulus": "^3.0.0" }, "peerDependenciesMeta": { @@ -50,7 +50,7 @@ } }, "devDependencies": { - "@googlemaps/js-api-loader": "^1.16.6", + "@googlemaps/js-api-loader": "^2.0.0", "@hotwired/stimulus": "^3.0.0", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.6.3", diff --git a/src/Map/src/Bridge/Google/assets/src/map_controller.ts b/src/Map/src/Bridge/Google/assets/src/map_controller.ts index 4372b09c23c..e5751aeb32d 100644 --- a/src/Map/src/Bridge/Google/assets/src/map_controller.ts +++ b/src/Map/src/Bridge/Google/assets/src/map_controller.ts @@ -9,8 +9,8 @@ /// -import type { LoaderOptions } from '@googlemaps/js-api-loader'; -import { Loader } from '@googlemaps/js-api-loader'; +import type { APIOptions } from '@googlemaps/js-api-loader'; +import { importLibrary, setOptions } from '@googlemaps/js-api-loader'; import type { CircleDefinition, Icon, @@ -39,8 +39,6 @@ type MapOptions = Pick< | 'fullscreenControlOptions' >; -let _google: typeof google; - // Loading the Google Maps API is an asynchronous operation, so we need to track the loading state to prevent race conditions. let _loading = false; let _loaded = false; @@ -65,7 +63,10 @@ export default class extends AbstractMapController< google.maps.RectangleOptions, google.maps.Rectangle > { - declare providerOptionsValue: Pick; + declare providerOptionsValue: Pick< + APIOptions, + 'key' | 'v' | 'language' | 'region' | 'libraries' | 'authReferrerPolicy' | 'mapIds' | 'channel' | 'solutionChannel' + >; declare map: google.maps.Map; @@ -83,32 +84,13 @@ export default class extends AbstractMapController< } _loading = true; - _google = { maps: {} as typeof google.maps }; let { libraries = [], ...loaderOptions } = this.providerOptionsValue; - const loader = new Loader(loaderOptions); + setOptions(loaderOptions); - // We could have used `loader.load()` to correctly load libraries, but this method is deprecated in favor of `loader.importLibrary()`. - // But `loader.importLibrary()` is not a 1-1 replacement for `loader.load()`, we need to re-build the `google.maps` object ourselves, - // see https://github.com/googlemaps/js-api-loader/issues/837 for more information. libraries = ['core', ...libraries.filter((library) => library !== 'core')]; // Ensure 'core' is loaded first - const librariesImplementations = await Promise.all(libraries.map((library) => loader.importLibrary(library))); - librariesImplementations.map((libraryImplementation, index) => { - if (typeof libraryImplementation !== 'object' || libraryImplementation === null) { - return; - } - - const library = libraries[index]; - - // The following libraries are in a sub-namespace - if (['marker', 'places', 'geometry', 'journeySharing', 'drawing', 'visualization'].includes(library)) { - // @ts-ignore - _google.maps[library] = libraryImplementation as any; - } else { - _google.maps = { ..._google.maps, ...libraryImplementation }; - } - }); + await Promise.all(libraries.map((library) => importLibrary(library))); _loading = false; _loaded = true; @@ -142,7 +124,7 @@ export default class extends AbstractMapController< } protected dispatchEvent(name: string, payload: Record = {}): void { - payload.google = _google; + payload.google = google; this.dispatch(name, { prefix: 'ux:map', detail: payload, @@ -158,7 +140,7 @@ export default class extends AbstractMapController< options.streetViewControl = typeof options.streetViewControlOptions !== 'undefined'; options.fullscreenControl = typeof options.fullscreenControlOptions !== 'undefined'; - return new _google.maps.Map(this.element, { + return new google.maps.Map(this.element, { center, zoom, minZoom, @@ -175,7 +157,7 @@ export default class extends AbstractMapController< }): google.maps.marker.AdvancedMarkerElement { const { '@id': _id, position, title, infoWindow, icon, bridgeOptions = {} } = definition; - const marker = new _google.maps.marker.AdvancedMarkerElement({ + const marker = new google.maps.marker.AdvancedMarkerElement({ position, title, map: this.map, @@ -208,7 +190,7 @@ export default class extends AbstractMapController< }): google.maps.Polygon { const { '@id': _id, points, infoWindow, bridgeOptions = {} } = definition; - const polygon = new _google.maps.Polygon({ + const polygon = new google.maps.Polygon({ paths: points, map: this.map, ...bridgeOptions, @@ -232,7 +214,7 @@ export default class extends AbstractMapController< }): google.maps.Polyline { const { '@id': _id, points, infoWindow, bridgeOptions = {} } = definition; - const polyline = new _google.maps.Polyline({ + const polyline = new google.maps.Polyline({ path: points, map: this.map, ...bridgeOptions, @@ -252,7 +234,7 @@ export default class extends AbstractMapController< protected doCreateCircle({ definition }: { definition: CircleDefinition }): google.maps.Circle { const { '@id': _id, center, radius, infoWindow, bridgeOptions = {} } = definition; - const circle = new _google.maps.Circle({ + const circle = new google.maps.Circle({ center, radius, map: this.map, @@ -277,8 +259,8 @@ export default class extends AbstractMapController< }): google.maps.Rectangle { const { northEast, southWest, infoWindow, bridgeOptions = {} } = definition; - const rectangle = new _google.maps.Rectangle({ - bounds: new _google.maps.LatLngBounds(southWest, northEast), + const rectangle = new google.maps.Rectangle({ + bounds: new google.maps.LatLngBounds(southWest, northEast), map: this.map, ...bridgeOptions, }); @@ -325,7 +307,7 @@ export default class extends AbstractMapController< ...bridgeOptions, }; - const infoWindow = new _google.maps.InfoWindow(infoWindowOptions); + const infoWindow = new google.maps.InfoWindow(infoWindowOptions); element.addListener('click', (event: google.maps.MapMouseEvent) => { if (autoClose) { diff --git a/src/Map/src/Bridge/Google/src/Renderer/GoogleRenderer.php b/src/Map/src/Bridge/Google/src/Renderer/GoogleRenderer.php index 331137ed4a5..6a8058949e3 100644 --- a/src/Map/src/Bridge/Google/src/Renderer/GoogleRenderer.php +++ b/src/Map/src/Bridge/Google/src/Renderer/GoogleRenderer.php @@ -25,23 +25,23 @@ final class GoogleRenderer extends AbstractRenderer { /** - * Parameters are based from https://googlemaps.github.io/js-api-loader/interfaces/LoaderOptions.html documentation. + * Parameters are based from https://github.com/googlemaps/js-api-loader/blob/107cf47/src/index.ts#L28-L38 APIOptions interface. */ public function __construct( StimulusHelper $stimulusHelper, UxIconRenderer $uxIconRenderer, - #[\SensitiveParameter] private readonly string $apiKey, - private readonly ?string $id = null, + #[\SensitiveParameter] private readonly string $key, + private readonly ?string $v = null, private readonly ?string $language = null, private readonly ?string $region = null, - private readonly ?string $nonce = null, - private readonly ?int $retries = null, - private readonly ?string $url = null, - private readonly ?string $version = null, /** * @var array<'core'|'maps'|'places'|'geocoding'|'routes'|'marker'|'geometry'|'elevation'|'streetView'|'journeySharing'|'drawing'|'visualization'> */ private readonly array $libraries = [], + private readonly ?string $authReferrerPolicy = null, + private readonly array $mapIds = [], + private readonly ?string $channel = null, + private readonly ?string $solutionChannel = null, private readonly ?string $defaultMapId = null, ) { parent::__construct($stimulusHelper, $uxIconRenderer); @@ -55,15 +55,15 @@ protected function getName(): string protected function getProviderOptions(): array { return array_filter([ - 'id' => $this->id, + 'v' => $this->v, 'language' => $this->language, 'region' => $this->region, - 'nonce' => $this->nonce, - 'retries' => $this->retries, - 'url' => $this->url, - 'version' => $this->version, 'libraries' => $this->libraries, - ]) + ['apiKey' => $this->apiKey]; + 'authReferrerPolicy' => $this->authReferrerPolicy, + 'mapIds' => $this->defaultMapId && !\in_array($this->defaultMapId, $this->mapIds, true) ? [...$this->mapIds, $this->defaultMapId] : $this->mapIds, + 'channel' => $this->channel, + 'solutionChannel' => $this->solutionChannel, + ]) + ['key' => $this->key]; } protected function getDefaultMapOptions(): MapOptionsInterface @@ -88,16 +88,16 @@ public function __toString(): string { return \sprintf( 'google://%s@default/?%s', - str_repeat('*', \strlen($this->apiKey)), + str_repeat('*', \strlen($this->key)), http_build_query(array_filter([ - 'id' => $this->id, + 'v' => $this->v, 'language' => $this->language, 'region' => $this->region, - 'nonce' => $this->nonce, - 'retries' => $this->retries, - 'url' => $this->url, - 'version' => $this->version, 'libraries' => $this->libraries, + 'authReferrerPolicy' => $this->authReferrerPolicy, + 'mapIds' => $this->mapIds, + 'channel' => $this->channel, + 'solutionChannel' => $this->solutionChannel, ])) ); } diff --git a/src/Map/src/Bridge/Google/src/Renderer/GoogleRendererFactory.php b/src/Map/src/Bridge/Google/src/Renderer/GoogleRendererFactory.php index 29f81edc3d0..774aafa3e2d 100644 --- a/src/Map/src/Bridge/Google/src/Renderer/GoogleRendererFactory.php +++ b/src/Map/src/Bridge/Google/src/Renderer/GoogleRendererFactory.php @@ -44,15 +44,15 @@ public function create(Dsn $dsn): RendererInterface return new GoogleRenderer( $this->stimulus, $this->uxIconRenderer, - apiKey: $apiKey, - id: $dsn->getOption('id'), + key: $apiKey, + v: $dsn->getOption('v', 'weekly'), language: $dsn->getOption('language'), region: $dsn->getOption('region'), - nonce: $dsn->getOption('nonce'), - retries: $dsn->getOption('retries'), - url: $dsn->getOption('url'), - version: $dsn->getOption('version', 'weekly'), libraries: ['maps', 'marker', ...$dsn->getOption('libraries', [])], + authReferrerPolicy: $dsn->getOption('authReferrerPolicy'), + mapIds: $dsn->getOption('mapIds', []), + channel: $dsn->getOption('channel'), + solutionChannel: $dsn->getOption('solutionChannel'), defaultMapId: $this->defaultMapId, ); } diff --git a/src/Map/src/Bridge/Google/tests/GoogleRendererFactoryTest.php b/src/Map/src/Bridge/Google/tests/GoogleRendererFactoryTest.php index 424b3a2943e..78c3a623e81 100644 --- a/src/Map/src/Bridge/Google/tests/GoogleRendererFactoryTest.php +++ b/src/Map/src/Bridge/Google/tests/GoogleRendererFactoryTest.php @@ -33,18 +33,18 @@ public static function supportsRenderer(): iterable public static function createRenderer(): iterable { yield [ - 'google://*******************@default/?version=weekly&libraries%5B0%5D=maps&libraries%5B1%5D=marker', + 'google://*******************@default/?v=weekly&libraries%5B0%5D=maps&libraries%5B1%5D=marker', 'google://GOOGLE_MAPS_API_KEY@default', ]; yield [ - 'google://*******************@default/?version=quartly&libraries%5B0%5D=maps&libraries%5B1%5D=marker', - 'google://GOOGLE_MAPS_API_KEY@default?version=quartly', + 'google://*******************@default/?v=quartly&libraries%5B0%5D=maps&libraries%5B1%5D=marker', + 'google://GOOGLE_MAPS_API_KEY@default?v=quartly', ]; yield [ - 'google://*******************@default/?version=quartly&libraries%5B0%5D=maps&libraries%5B1%5D=marker&libraries%5B2%5D=geometry', - 'google://GOOGLE_MAPS_API_KEY@default?version=quartly&libraries[]=geometry', + 'google://*******************@default/?v=quartly&libraries%5B0%5D=maps&libraries%5B1%5D=marker&libraries%5B2%5D=geometry', + 'google://GOOGLE_MAPS_API_KEY@default?v=quartly&libraries[]=geometry', ]; } diff --git a/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php b/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php index 33432cd7188..41c509d8401 100644 --- a/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php +++ b/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php @@ -39,23 +39,23 @@ public static function provideTestRenderMap(): iterable $marker3 = new Marker(position: new Point(45.8566, 2.3522), title: 'Dijon', id: 'marker3'); yield 'simple map, with minimum options' => [ - 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), apiKey: 'api_key'), + 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), key: 'api_key'), 'map' => $map, ]; yield 'with every options' => [ - 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), apiKey: 'api_key', id: 'gmap', language: 'fr', region: 'FR', nonce: 'abcd', retries: 10, url: 'https://maps.googleapis.com/maps/api/js', version: 'quarterly'), + 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), key: 'api_key', v: 'quarterly', language: 'fr', region: 'FR', libraries: ['places', 'geometry'], authReferrerPolicy: 'origin', mapIds: ['MyMapId1', 'MyMapId2'], channel: 'my-channel', solutionChannel: 'my-solution-channel'), 'map' => $map, ]; yield 'with custom attributes' => [ - 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), apiKey: 'api_key'), + 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), key: 'api_key'), 'map' => $map, 'attributes' => ['data-controller' => 'my-custom-controller', 'class' => 'map'], ]; yield 'with markers and infoWindows' => [ - 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), apiKey: 'api_key'), + 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), key: 'api_key'), 'map' => (new Map()) ->center(new Point(48.8566, 2.3522)) ->zoom(12) @@ -64,7 +64,7 @@ public static function provideTestRenderMap(): iterable ]; yield 'with all markers removed' => [ - 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), apiKey: 'api_key'), + 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), key: 'api_key'), 'map' => (new Map()) ->center(new Point(48.8566, 2.3522)) ->zoom(12) @@ -75,7 +75,7 @@ public static function provideTestRenderMap(): iterable ]; yield 'with marker remove and new ones added' => [ - 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), apiKey: 'api_key'), + 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), key: 'api_key'), 'map' => (new Map()) ->center(new Point(48.8566, 2.3522)) ->zoom(12) @@ -86,7 +86,7 @@ public static function provideTestRenderMap(): iterable ]; yield 'with polygons and infoWindows' => [ - 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), apiKey: 'api_key'), + 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), key: 'api_key'), 'map' => (new Map()) ->center(new Point(48.8566, 2.3522)) ->zoom(12) @@ -95,7 +95,7 @@ public static function provideTestRenderMap(): iterable ]; yield 'with polylines and infoWindows' => [ - 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), apiKey: 'api_key'), + 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), key: 'api_key'), 'map' => (new Map()) ->center(new Point(48.8566, 2.3522)) ->zoom(12) @@ -104,7 +104,7 @@ public static function provideTestRenderMap(): iterable ]; yield 'with circles and infoWindows' => [ - 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), apiKey: 'api_key'), + 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), key: 'api_key'), 'map' => (new Map()) ->center(new Point(48.8566, 2.3522)) ->zoom(12) @@ -113,7 +113,7 @@ public static function provideTestRenderMap(): iterable ]; yield 'with rectangles and infoWindows' => [ - 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), apiKey: 'api_key'), + 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), key: 'api_key'), 'map' => (new Map()) ->center(new Point(48.8566, 2.3522)) ->zoom(12) @@ -122,7 +122,7 @@ public static function provideTestRenderMap(): iterable ]; yield 'with controls enabled' => [ - 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), apiKey: 'api_key'), + 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), key: 'api_key'), 'map' => (new Map()) ->center(new Point(48.8566, 2.3522)) ->zoom(12) @@ -135,7 +135,7 @@ public static function provideTestRenderMap(): iterable ]; yield 'without controls enabled' => [ - 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), apiKey: 'api_key'), + 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), key: 'api_key'), 'map' => (new Map()) ->center(new Point(48.8566, 2.3522)) ->zoom(12) @@ -190,7 +190,7 @@ public function renderIcon(string $name, array $attributes = []): string ]; yield 'with map extra data' => [ - 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), apiKey: 'api_key'), + 'renderer' => new GoogleRenderer(new StimulusHelper(null), new UxIconRenderer(null), key: 'api_key'), 'map' => (new Map()) ->center(new Point(48.8566, 2.3522)) ->zoom(12) diff --git a/src/Map/src/Bridge/Google/tests/__snapshots__/GoogleRendererTest__testRenderMap with data set markers with icons__1.txt b/src/Map/src/Bridge/Google/tests/__snapshots__/GoogleRendererTest__testRenderMap with data set markers with icons__1.txt index 19bc46ce8e4..1aec5da39ef 100644 --- a/src/Map/src/Bridge/Google/tests/__snapshots__/GoogleRendererTest__testRenderMap with data set markers with icons__1.txt +++ b/src/Map/src/Bridge/Google/tests/__snapshots__/GoogleRendererTest__testRenderMap with data set markers with icons__1.txt @@ -2,7 +2,7 @@ Run "php vendor/bin/phpunit -d --update-snapshots" to update the snapshot. -->