Repository navigation
feat: places-api-new-migration - #711
jeffersontuc wants to merge 15 commits into
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fetches suggestions with AutocompleteSuggestion and session tokens, renders an accessible .pac-container dropdown under the existing Input, resolves the selection with Place.fetchFields and feeds it through the legacy-shape adapter. Falls back page-wide to the legacy Autocomplete/SearchBox widgets when the new API is not enabled or not available. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Hi! I'm VTEX IO CI/CD Bot and I'll be helping you to publish your app! 🤖 Please select which version do you want to release:
And then you just need to merge your PR when you are ready! There is no need to create a release commit/tag.
|
DK Review — Deferred (Sonar gate)⏭️ LLM review not run: this PR's Sonar quality gate is currently failing. DK Review holds the AI review until Sonar passes, so review budget is not spent on a PR that already needs changes. Fix the issues Sonar reported and push again — the full review runs automatically once Sonar is green.
This PR is not blocked by DK Review; a human engineer can still approve and merge manually. DK Review v1.0.0 |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Places API (New) rejects with an RpcError whose code is the numeric gRPC status (7) and whose reason (e.g. API_KEY_SERVICE_BLOCKED) only appears in the grpc-web status details metadata, so keys that block the new API never triggered the legacy fallback. Classify numeric PERMISSION_DENIED and read the ErrorInfo reason from the metadata for the Splunk event. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The legacy widget rendered Google's attribution itself; the custom dropdown used with Places API (New) must show it too. The list stays a pure listbox inside the .pac-container wrapper, with the attribution after it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tore Places API (New) is now used only when the page sets window.vtex.googlePlacesApi = 'new' (meant to come from the store's site configuration, like googleMapsApiKey). Any other value, a missing window.vtex or the unrendered template placeholder keeps the legacy widgets and the legacy rendered tree, so no store changes Google APIs, look or billing without the owner's action. Opted-in stores keep the fallback to the legacy widgets when their key blocks the new API. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Remove the runtime fallback from Places API (New) to the legacy widgets. An opted-in store always uses Places API (New); when its key, project or Maps version cannot serve it, one 'places-api-new-error' event per page is sent to Splunk with the reason instead of silently switching APIs (and billing). Stores that did not opt in are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Read the store preference once per GeolocationInput instance and drop the runtime-mode guards that only existed for the removed fallback. - Reduce error classification to what the RpcError actually carries (code 7, or an API_KEY_* / SERVICE_DISABLED reason); it only feeds telemetry now. - Rename placesMode.ts to placesApi.ts. - Generate the listbox id with lodash/uniqueId. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Summary
Implements
specs/places-api-new-migration.md. The legacy Places widgets stay the default. Google Places API (New) is used only by stores that opt in throughwindow.vtex.googlePlacesApi = 'new', so no store changes Google APIs, look or billing without the owner's action. This fixes stores whose Google Cloud project can no longer enable the legacy Places API (LegacyApiNotActivatedMapError), once they opt in.The value is meant to come from the store's site configuration, exposed by the Checkout page next to
window.vtex.googleMapsApiKey(portal v2SiteConfiguration→ConfigContextProvider→vcs.checkout-ui/src/index.html). Exposing it is a separate change in those repos; until then this PR is inert in production.No public API changes:
index.ts,components.ts,inputs.ts,types/rules.ts,react/country/*.tsand the exported component props are untouched. No new dependencies, no new i18n keys.'new'(trimmed, case-insensitive) enables the new API. A missingwindow.vtex,'legacy', any other value or the unrendered{{ config.googlePlacesApi }}placeholder keeps the legacy widget, renders only theInput(same tree as before), makes no Places API (New) request and emits no Splunk event.AutocompleteSuggestion.fetchAutocompleteSuggestionswith anAutocompleteSessionTokenandincludedRegionCodes: [rules.abbr]. A selection is resolved withPlace.fetchFields(['addressComponents','formattedAddress','location'])and converted to the legacyPlaceResultshape bytoLegacyPlaceResult, then goes through the unchangedgeolocationAutoCompleteAddress. Each completed selection starts a new session token.RpcErrorcode7, e.g.API_KEY_SERVICE_BLOCKED), or its classes are missing from the loaded Maps version, the list stays closed and one Splunk event per page is sent (workflowInstance: 'places-api-new-error', with the reason, account and country) so the store configuration can be fixed. Transient errors (network14, quota8) are not reported.PlaceSuggestionsdropdown with.pac-container/.pac-itemclasses, kept for the omnishipping Cypress selectors and merchant CSS. It supports ArrowUp/ArrowDown with wrap-around, Enter, Escape and close on blur, and follows the WAI-ARIA combobox pattern (role,aria-expanded,aria-controls,aria-activedescendant). It also shows the Google Maps attribution (.pac-attribution, text form) required for Places data displayed outside a Google map, which the legacy widget used to render itself. The attribution sits after the list and outside thelistbox.useSearchBox: Enter with no active option selects the first suggestion. If there are no suggestions, it falls back toPlace.searchByText. WithoutuseSearchBox, Enter does nothing, as before.New internal modules under
react/geolocation/:placesTypes.ts,placesAdapter.ts,newPlacesSource.ts,placesApi.ts(store preference, new-API detection, error reporting),PlaceSuggestions.tsx.react/metrics.tsgainslogPlacesApiNewErrorand thegooglePlacesApityping; the existing calls are unchanged.Tests
placesAdapter.test.ts: field mapping, missing location and components, parity with the legacy flow throughgeolocationAutoCompleteAddress, and the ARG CABA handler.newPlacesSource.test.ts: request shape (lowercased region, no region whenabbris null), prediction mapping, Essentials-only fields,searchByText.placesApi.test.ts: store preference parsing (missing,legacy,new, casing, unrendered placeholder), new-API class detection, error reported to Splunk once per page, classification of configuration vs. transientRpcErrors (blocked key, disabled service, invalid key vs. network and quota) and reason extraction.PlaceSuggestions.test.tsx: roles and classes, mousedown selection withpreventDefault, hover, style injection.GeolocationInput.test.tsx(RTL): the existing legacy tests still pass; default (no opt-in,legacy, unrendered placeholder) keeps the legacy widget with no new-API request or Splunk event; opted-in tests cover every row of the spec's Key Scenarios table (happy path, session tokens, keyboard, Escape and blur, debounce, stale responses, no results, no location, unmount during a request, country change,loadingGoogle, blocked key without switching APIs, SearchBox not attached, missing classes, transient errors,useSearchBoxEnter behavior).yarn --cwd react testgives 39 suites and 268 tests passing (baseline 216).yarn lint:localespasses,yarn --cwd react buildsucceeds,tsc --noEmitreports 0 errors, and ESLint reports no errors on the touched files.Assumptions
fetchAutocompleteSuggestionsgets no explicitlanguage. The Maps script is already loaded withlanguage: localebygoogleMaps.js, and the new API uses it by default.fetchSuggestionsstill accepts an optionallanguage.render, which needs React 16+. On React 15 (still in the peer range),GeolocationInputstays on the legacy widgets.react-domis not a declared dependency, so there is no portal. The list is an absolutely positioned sibling right afterInput(notop/left, so it sits at its static position below the input), with the input's width andz-index: 1000. In legacy mode nothing extra renders, so the DOM is identical to today..pac-*stylesheet close to Google's defaults is injected once and prepended to<head>, so merchant CSS keeps precedence.unsupported-maps-versiononce; it does not switch to legacy.Deviations
searchByTextparameter: the spec namesincludedRegionCode, which is not a field of the Maps JSSearchByTextRequest. The implementation usesregion(lowercasedrules.abbr) and also passesmaxResultCount: 1.RpcErrorwith a numeric gRPCcode(7=PERMISSION_DENIED). The reason (API_KEY_SERVICE_BLOCKED,SERVICE_DISABLED) only appears inmetadata['grpc-web-status-details-bin'], not as string codes.isPlacesApiNewConfigurationErrorreports code7or anAPI_KEY_*/SERVICE_DISABLEDreason; network (14) and quota (8) errors are not reported.googleMapsastypeof google.maps, but the installed@types/google.maps@3.49does not have the Places API (New) classes and upgrading is not allowed. The modules use minimal local interfaces (placesTypes.ts) instead.PLACE_FIELDS,resetPlacesApiNewErrorReport(test-only),ensureSuggestionStyles,getOptionId.Follow-ups
googlePlacesApifield in portal v2SiteConfiguration(validated, exposed byConfigContextProvider),vtex.googlePlacesApi = "{{ config.googlePlacesApi }}" || undefinedinvcs.checkout-ui/src/index.html, and a field in the portal-ui site configuration admin whose help text says the key must allow Places API (New) before the option is enabled (there is no fallback). Until then, test by settingwindow.vtex.googlePlacesApi = 'new'before the page loadsGeolocationInput.'auto'value: keep the legacy widget and probe the legacy status only when no suggestions appear.vtex linkinto an omnishipping workspace, running the geolocation delivery, pickup and invoice flows, including the Cypress specs that click.pac-item;StyleguideInputand with merchant CSS.places-api-new-errorevents after stores start opting in; each one is a store whose key, project or Maps version needs fixing.searchByText: now documented in the spec's Cost NFR (Text Search is billed per request under its own SKU, outside the autocomplete session). Include it in the billing check; this came from the DK review on spec: places-api-new-migration #710.Spec
This PR carries the spec together with the implementation:
specs/places-api-new-migration.md(first two commits). The separate spec PR #710 was closed in favor of this one.Draft→Approved→Done. The spec was reviewed and approved by @jeffersontuc before implementation started. This addresses the DK review RESTRICT finding on spec: places-api-new-migration #710 ("Status: Approved without evidence of review").react/geolocation/in one repo, and consumers need no code changes. The omnishipping step is QA on a linked workspace, not coordinated changes there.🤖 Generated with Claude Code