I’m using Google Maps AutoComplete for my website, and so far it’s worked just fine. However, it seems that Google AutoComplete does not work when inside an Accordion component from Shadcn. No autocomplete options will show up, and also using the arrow keys and pressing enter does nothing either.
Here’s my code for a minimal reproducible example:
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "../components/ui/accordion"
import GoogleMaps from "../components/googleMap";
import { Input } from "../components/ui/input";
import { useEffect, useRef, useState } from "react";
export default function AccountSettings() {
const autoCompleteRef = useRef<HTMLInputElement>(null);
const [autoCompleteInstance, setAutoCompleteInstance] = useState<HTMLInputElement | null>(null);
useEffect(() => {
// Ensure that the autoCompleteInstance is set before rendering the GoogleMaps component
setAutoCompleteInstance(autoCompleteRef.current);
}, []);
console.log(autoCompleteInstance)
return (
<div>
<Accordion type="single" collapsible className="w-full">
<AccordionItem value="item-1" className="border-none w-full">
<AccordionTrigger className="flex justify-between w-full h-10">
<span>Location Accordion</span>
</AccordionTrigger>
<AccordionContent>
<Input ref={autoCompleteRef} placeholder="Choose a location" />
{(autoCompleteInstance) &&
<GoogleMaps
radius={1}
locationField={autoCompleteInstance}
initialLat={0}
initialLng={0}
updateLat={(lat) => { }}
updateLng={(lng) => { }}
/>
}
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
)
}
And here is my googleMap.tsx file, which sets up everything for the google map:
import { useRef, useState, useEffect } from "react";
declare global {
interface Window {
initMap: () => void;
}
}
export default function GoogleMaps(
{ radius, locationField, initialLat, initialLng, updateLat, updateLng }:
{
radius: number;
locationField: HTMLInputElement | null;
initialLat: number;
initialLng: number;
updateLat: (lat: number) => void;
updateLng: (lng: number) => void;
}) {
const mapRef = useRef<HTMLDivElement>(null);
const [map, setMap] = useState<google.maps.Map | null>(null);
const [marker, setMarker] = useState<google.maps.Marker | null>(null);
const [circle, setCircle] = useState<google.maps.Circle | null>(null);
// marker and circle refs are required to remove old markers and circles
const markerRef = useRef<google.maps.Marker | null>(null);
const circleRef = useRef<google.maps.Circle | null>(null);
const radiusRef = useRef(radius);
useEffect(() => {
markerRef.current = marker;
}, [marker]);
useEffect(() => {
circleRef.current = circle;
}, [circle]);
// Update lat and lng in parent component when marker is moved
useEffect(() => {
if (marker) {
updateLat(marker.getPosition()?.lat()!);
updateLng(marker.getPosition()?.lng()!);
}
}, [marker, radius]);
useEffect(() => {
radiusRef.current = radius;
if (circle) {
if (radius === null || radius === 0) {
circle.setMap(null);
} else {
circle.setMap(map);
circle.setRadius(radius! * 1000);
}
}
}, [radius])
useEffect(() => {
window.initMap = initMap;
if (window.google && window.google.maps) {
initMap();
}
}, []);
const initMap = () => {
if (locationField === null) {
console.error("AutoCompleteInstance is null");
return;
}
const initialLocation = { lat: initialLat, lng: initialLng };
const mapInstance = new window.google.maps.Map(mapRef.current!, {
center: initialLocation,
zoom: 8,
streetViewControl: false,
});
mapInstance.setZoom(12);
setMap(mapInstance);
// Create a marker at the initial location
const initialMarker = new window.google.maps.Marker({
position: initialLocation,
map: mapInstance,
});
setMarker(initialMarker);
setCircle(getNewCircle(mapInstance, initialLocation));
const autoComplete = new window.google.maps.places.Autocomplete(
locationField
);
autoComplete.bindTo("bounds", mapInstance);
autoComplete.setFields(["geometry"]);
autoComplete.setTypes(['address']);
autoComplete.addListener("place_changed", () => {
const place = autoComplete.getPlace();
if (!place.geometry || !place.geometry.location) {
return;
}
const newLocation = {
lat: place.geometry.location.lat(),
lng: place.geometry.location.lng(),
};
mapInstance.setCenter(newLocation);
mapInstance.setZoom(12);
// Remove old marker and circle
if (markerRef.current) {
markerRef.current.setMap(null);
}
if (circleRef.current) {
circleRef.current.setMap(null);
}
const newMarker = new window.google.maps.Marker({
position: newLocation,
map: mapInstance,
});
setMarker(newMarker);
setCircle(getNewCircle(mapInstance, newLocation));
});
};
const getNewCircle = (mapInstance: google.maps.Map, location: { lat: number; lng: number; }) => {
return new window.google.maps.Circle({
strokeColor: "#FF0000",
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: "#FF0000",
fillOpacity: 0.35,
map: mapInstance,
center: location,
radius: radiusRef.current * 1000, // Radius in meters
});
}
return (
<div>
<div ref={mapRef} style={{ height: "400px" }} />
</div>
);
}
If I place the autocomplete input outside of the accordion, it works just fine. So how do I get autocomplete to work inside the Accordion?