Skip to content

Commit 97702cf

Browse files
fix: Quality and standards improvements for Place AC session. (#766)
* fix: Quality and standards improvements. Change-Id: Ica31d9553f244961a67360655e5ca1e40510d324 * Update samples/place-autocomplete-data-session/index.ts Co-authored-by: noelle-jung <[email protected]> * Enhance gmp-map with additional attributes and classes * Refactor CSS selectors to use class syntax * Refactor autocomplete session handling and cleanup * Adds session token counter. * Prettier * Ran prettier; removed commented code Removed unused listener for map loading event. * Ran prettier formatting * Prettier Format CSS for consistency and readability * Review code changes * Review changes * Remove more id (forgot on the first pass) * Replace anchor with button for accessibility * Add link-button styles for improved accessibility * Remove language and region from autocomplete config Removed language and region properties from the configuration. * Fixes from Will and Chris collab session * Remove refreshToken call in autocomplete setup Removed unnecessary refreshToken call before adding event listener. Previously the token count had been incremented by 1 before the user took an action; the count should reflect the number of selections the user made. * Update samples/place-autocomplete-data-session/index.ts Co-authored-by: noelle-jung <[email protected]> * Applied review comments * Refactor CSS for place button and layout styles * Incorporated * Update styles for place-button and results Refactor styles for place-button and results list. --------- Co-authored-by: noelle-jung <[email protected]>
1 parent 6214773 commit 97702cf

File tree

3 files changed

+170
-83
lines changed

3 files changed

+170
-83
lines changed

samples/place-autocomplete-data-session/index.html

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,34 @@
66
-->
77
<!-- [START maps_place_autocomplete_data_session] -->
88
<html>
9-
<head>
10-
<title>Place Autocomplete Data API Session</title>
9+
<head>
10+
<title>Place Autocomplete Data API Session</title>
1111

12-
<link rel="stylesheet" type="text/css" href="./style.css" />
13-
<script type="module" src="./index.js"></script>
14-
</head>
15-
<body>
16-
<!-- // [START maps_place_autocomplete_data_session_html] -->
17-
<input id="input" type="text" placeholder="Search for a place..." />
18-
<div id="title"></div>
19-
<ul id="results"></ul>
20-
<img
21-
class="powered-by-google"
22-
src="https://storage.googleapis.com/geo-devrel-public-buckets/powered_by_google_on_white.png"
23-
alt="Powered by Google"
24-
/>
25-
<!-- // [END maps_place_autocomplete_data_session_html] -->
26-
27-
<!--
28-
The `defer` attribute causes the script to execute after the full HTML
29-
document has been parsed. For non-blocking uses, avoiding race conditions,
30-
and consistent behavior across browsers, consider loading using Promises. See
31-
https://developers.google.com/maps/documentation/javascript/load-maps-js-api
32-
for more information.
33-
-->
34-
<script
35-
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyA6myHzS10YXdcazAFalmXvDkrYCp5cLc8&callback=init&libraries=places&v=weekly"
36-
defer
37-
></script>
38-
</body>
12+
<link rel="stylesheet" type="text/css" href="./style.css" />
13+
<script type="module" src="./index.js"></script>
14+
<!-- prettier-ignore -->
15+
<script>(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})
16+
({key: "AIzaSyA6myHzS10YXdcazAFalmXvDkrYCp5cLc8", v: "weekly"});</script>
17+
</head>
18+
<body>
19+
<!-- // [START maps_place_autocomplete_data_session_html] -->
20+
<gmp-map center="37.7893, -122.4039" zoom="12" map-id="DEMO_MAP_ID">
21+
<div
22+
class="controls"
23+
slot="control-inline-start-block-start"
24+
>
25+
<input
26+
type="text"
27+
class="input"
28+
placeholder="Search for a place..."
29+
autocomplete="off"
30+
/><!-- Turn off the input's own autocomplete (not supported by all browsers).-->
31+
<div class="token-status"></div>
32+
<div class="title"></div>
33+
<ol class="results"></ol>
34+
</div>
35+
</gmp-map>
36+
<!-- // [END maps_place_autocomplete_data_session_html] -->
37+
</body>
3938
</html>
4039
<!-- [END maps_place_autocomplete_data_session] -->

samples/place-autocomplete-data-session/index.ts

Lines changed: 96 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,97 +5,144 @@
55
*/
66

77
// [START maps_place_autocomplete_data_session]
8-
let titleElement;
9-
let resultsContainerElement;
10-
let inputElement;
11-
8+
const mapElement = document.querySelector('gmp-map') as google.maps.MapElement;
9+
let innerMap: google.maps.Map;
10+
let marker: google.maps.marker.AdvancedMarkerElement;
11+
let titleElement = document.querySelector('.title') as HTMLElement;
12+
let resultsContainerElement = document.querySelector('.results') as HTMLElement;
13+
let inputElement = document.querySelector('input') as HTMLInputElement;
14+
let tokenStatusElement = document.querySelector('.token-status') as HTMLElement;
1215
let newestRequestId = 0;
16+
let tokenCount = 0;
1317

14-
// Add an initial request body.
15-
const request = {
18+
// Create an initial request body.
19+
const request: google.maps.places.AutocompleteRequest = {
1620
input: '',
17-
locationRestriction: { west: -122.44, north: 37.8, east: -122.39, south: 37.78 },
18-
origin: { lat: 37.7893, lng: -122.4039 },
19-
includedPrimaryTypes: ['restaurant'],
20-
language: 'en-US',
21-
region: 'us',
22-
};
23-
24-
function init() {
25-
titleElement = document.getElementById('title');
26-
resultsContainerElement = document.getElementById('results');
27-
inputElement = document.querySelector('input');
21+
includedPrimaryTypes: [
22+
'restaurant',
23+
'cafe',
24+
'museum',
25+
'park',
26+
'botanical_garden',
27+
],
28+
}
29+
30+
async function init() {
31+
await google.maps.importLibrary('maps');
32+
innerMap = mapElement.innerMap;
33+
innerMap.setOptions({
34+
mapTypeControl: false,
35+
});
36+
37+
// Update request center and bounds when the map bounds change.
38+
google.maps.event.addListener(innerMap, 'bounds_changed', async () => {
39+
request.locationRestriction = innerMap.getBounds();
40+
request.origin = innerMap.getCenter();
41+
});
42+
2843
inputElement.addEventListener('input', makeAutocompleteRequest);
29-
refreshToken(request);
3044
}
3145

3246
async function makeAutocompleteRequest(inputEvent) {
33-
// Reset elements and exit if an empty string is received.
34-
if (inputEvent.target.value == '') {
35-
titleElement.innerText = '';
47+
// To avoid race conditions, store the request ID and compare after the request.
48+
const requestId = ++newestRequestId;
49+
50+
const { AutocompleteSuggestion } = (await google.maps.importLibrary(
51+
'places'
52+
)) as google.maps.PlacesLibrary;
53+
54+
if (!inputEvent.target?.value) {
55+
titleElement.textContent = '';
3656
resultsContainerElement.replaceChildren();
3757
return;
3858
}
3959

4060
// Add the latest char sequence to the request.
41-
request.input = inputEvent.target.value;
42-
43-
// To avoid race conditions, store the request ID and compare after the request.
44-
const requestId = ++newestRequestId;
61+
request.input = (inputEvent.target as HTMLInputElement).value;
4562

4663
// Fetch autocomplete suggestions and show them in a list.
47-
// @ts-ignore
48-
const { suggestions } = await google.maps.places.AutocompleteSuggestion.fetchAutocompleteSuggestions(request);
64+
const { suggestions } =
65+
await AutocompleteSuggestion.fetchAutocompleteSuggestions(request);
4966

5067
// If the request has been superseded by a newer request, do not render the output.
5168
if (requestId !== newestRequestId) return;
5269

53-
titleElement.innerText = `Query predictions for "${request.input}"`;
70+
titleElement.innerText = `Place predictions for "${request.input}"`;
5471

5572
// Clear the list first.
5673
resultsContainerElement.replaceChildren();
5774

5875
for (const suggestion of suggestions) {
5976
const placePrediction = suggestion.placePrediction;
6077

78+
if (!placePrediction) {
79+
continue;
80+
}
81+
6182
// Create a link for the place, add an event handler to fetch the place.
62-
const a = document.createElement('a');
63-
a.addEventListener('click', () => {
64-
onPlaceSelected(placePrediction!.toPlace());
83+
// We are using a button element to take advantage of its a11y capabilities.
84+
const placeButton = document.createElement('button');
85+
placeButton.addEventListener('click', () => {
86+
onPlaceSelected(placePrediction.toPlace());
6587
});
66-
a.innerText = placePrediction!.text.toString();
88+
placeButton.textContent = placePrediction.text.toString();
89+
placeButton.classList.add('place-button');
6790

6891
// Create a new list item element.
6992
const li = document.createElement('li');
70-
li.appendChild(a);
93+
li.appendChild(placeButton);
7194
resultsContainerElement.appendChild(li);
7295
}
7396
}
7497

7598
// Event handler for clicking on a suggested place.
76-
async function onPlaceSelected(place) {
99+
async function onPlaceSelected(place: google.maps.places.Place) {
100+
const { AdvancedMarkerElement } = (await google.maps.importLibrary(
101+
'marker'
102+
)) as google.maps.MarkerLibrary;
103+
77104
await place.fetchFields({
78-
fields: ['displayName', 'formattedAddress'],
105+
fields: ['displayName', 'formattedAddress', 'location'],
79106
});
80-
const placeText = document.createTextNode(`${place.displayName}: ${place.formattedAddress}`);
81-
resultsContainerElement.replaceChildren(placeText);
82-
titleElement.innerText = 'Selected Place:';
107+
108+
resultsContainerElement.textContent = `${place.displayName}: ${place.formattedAddress}`;
109+
titleElement.textContent = 'Selected Place:';
83110
inputElement.value = '';
84-
refreshToken(request);
111+
112+
await refreshToken();
113+
114+
// Remove the previous marker, if it exists.
115+
if (marker) {
116+
marker.remove();
117+
}
118+
119+
// Create a new marker.
120+
marker = new AdvancedMarkerElement({
121+
map: innerMap,
122+
position: place.location,
123+
title: place.displayName,
124+
})
125+
126+
// Center the map on the selected place.
127+
if (place.location) {
128+
innerMap.setCenter(place.location);
129+
innerMap.setZoom(15);
130+
}
85131
}
86132

87133
// Helper function to refresh the session token.
88-
function refreshToken(request) {
134+
async function refreshToken() {
135+
const { AutocompleteSessionToken } = (await google.maps.importLibrary(
136+
'places'
137+
)) as google.maps.PlacesLibrary;
138+
139+
// Increment the token counter.
140+
tokenCount++;
141+
89142
// Create a new session token and add it to the request.
90-
request.sessionToken = new google.maps.places.AutocompleteSessionToken();
143+
request.sessionToken = new AutocompleteSessionToken();
144+
tokenStatusElement.textContent = `Session token count: ${tokenCount}`;
91145
}
92146

93-
declare global {
94-
interface Window {
95-
init: () => void;
96-
}
97-
}
98-
window.init = init;
147+
init();
99148
// [END maps_place_autocomplete_data_session]
100-
void 0; // No-op to preserve the last region tag comment.
101-
export { };

samples/place-autocomplete-data-session/style.css

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,55 @@ body {
2222
padding: 0;
2323
}
2424

25-
a {
25+
.place-button {
26+
height: 3rem;
27+
width: 100%;
28+
background-color: transparent;
29+
text-align: left;
30+
border: none;
2631
cursor: pointer;
27-
text-decoration: underline;
28-
color: blue;
2932
}
3033

31-
input {
34+
.place-button:focus-visible {
35+
outline: 2px solid #0056b3;
36+
border-radius: 2px;
37+
}
38+
39+
.input {
3240
width: 300px;
41+
font-size: small;
42+
margin-bottom: 1rem;
43+
}
44+
45+
/* Styles for the floating panel */
46+
.controls {
47+
background-color: #fff;
48+
border-radius: 8px;
49+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
50+
font-family: sans-serif;
51+
font-size: small;
52+
margin: 12px;
53+
padding: 1rem;
54+
}
55+
56+
.title {
57+
font-weight: bold;
58+
margin-top: 1rem;
59+
margin-bottom: 0.5rem;
60+
}
61+
62+
.results {
63+
list-style-type: none;
64+
margin: 0;
65+
padding: 0;
66+
}
67+
68+
.results li:not(:last-child) {
69+
border-bottom: 1px solid #ddd;
70+
}
71+
72+
.results li:hover {
73+
background-color: #eee;
3374
}
3475

35-
/* [END maps_place_autocomplete_data_session] */
76+
/* [END maps_place_autocomplete_data_session] */

0 commit comments

Comments
 (0)