Advanced - Search This Area | Yext Hitchhikers Platform
What You’ll Learn
In this section, you will:
- Display a Search This Area button on the map when the map is dragged
- Trigger a search for locations on an area when the button is clicked
Overview
Many locators, such as Yelp and Google Maps, enable users to initiate a new search after the visible map boundaries have changed in response to when the user drags on the map. In this section, you will update your locator to mimic this behavior, by using the MapboxMap
onDrag
prop to display a “Search This Area” button when the center of the map changes.
1. Add Imports and State Values
Add imports for the LngLat
and LngLatBounds
types from Mapbox GL and the OnDragHandler
type from @yext/search-ui-react
:
// src/components/StoreLocator.tsx
import {
MapboxMap,
FilterSearch,
OnSelectParams,
VerticalResults,
getUserLocation,
OnDragHandler, // New
} from "@yext/search-ui-react";
import { useEffect, useState } from "react";
import { BiLoaderAlt } from "react-icons/bi";
import {
Matcher,
SelectableStaticFilter,
useSearchActions,
useSearchState,
} from "@yext/search-headless-react";
// Mapbox CSS bundle
import "mapbox-gl/dist/mapbox-gl.css";
import LocationCard from "./LocationCard";
import MapPin from "./MapPin";
import { LngLat, LngLatBounds } from "mapbox-gl"; // New
// component code...
Then, in the component, grab the resultCount
from the search state and create some new state variables using the useState
hook:
// src/components/StoreLocator.tsx
// imports...
const StoreLocator = (): JSX.Element => {
const resultCount = useSearchState(
(state) => state.vertical.resultsCount || 0
);
const [showSearchAreaButton, setShowSearchAreaButton] = useState(false);
const [mapCenter, setMapCenter] = useState<LngLat | undefined>();
const [mapBounds, setMapBounds] = useState<LngLatBounds | undefined>();
// other code from previous units...
}
You will be using these new variables in future steps.
2. Add the handleDrag Function
You need an onDrag
handler that MapboxMap
will call when the center of the map changes. Add the handleDrag
function to your component:
// src/components/StoreLocator.tsx
// imports...
const StoreLocator = (): JSX.Element => {
// other code from previous units/steps
const handleDrag: OnDragHandler = (center: LngLat, bounds: LngLatBounds) => {
setMapCenter(center);
setMapBounds(bounds);
setShowSearchAreaButton(true);
};
// other code from previous units/steps
}
handleDrag
will set the current map center and bounds and toggle the showSearchAreaButton
variable to true.
3. Add the handleSearchAreaClick Function
You’re eventually going to add a “Search This Area” button that will appear over top of the map if the center of the map changes. Before you add the button, add the handleSearchAreaClick
handler function to the component:
// src/components/StoreLocator.tsx
// imports...
const StoreLocator = (): JSX.Element => {
// other code from previous units/steps
const handleSearchAreaClick = () => {
if (mapCenter && mapBounds) {
const locationFilter: SelectableStaticFilter = {
selected: true,
displayName: "Current map area",
filter: {
kind: "fieldValue",
fieldId: "builtin.location",
value: {
lat: mapCenter.lat,
lng: mapCenter.lng,
radius: mapBounds.getNorthEast().distanceTo(mapCenter),
},
matcher: Matcher.Near,
},
};
searchActions.setStaticFilters([locationFilter]);
searchActions.executeVerticalQuery();
setShowSearchAreaButton(false);
}
};
// other code from previous units/steps
};
handleSearchArea
sets a filter in the search state based on the distance between the center and northeast corner of the map before running a new vertical query. Finally, it toggles showSearchAreaButton
to false.
4. Render the Search This Area Button
The last step is to conditionally render to the “Search This Area” button on top of the map if the center of the map changed. The new button will use handleSearchAreaClick
as its onClick
handler.
In addition, sometimes a given area won’t have any locations. To handle this, you will display “No results found for this area” when there are no results in the given map area.
Swap out the template code with the following to add the button:
// src/components/StoreLocator.tsx
// imports...
const StoreLocator = (): JSX.Element => {
// other code from previous units/steps
return (
<>
<div className="relative flex h-[calc(100vh-210px)] border">
{initialSearchState !== "complete" && (
<div className="absolute z-20 flex h-full w-full items-center justify-center bg-white opacity-70">
<BiLoaderAlt className="animate-spin " size={64} />
</div>
)}
<div className="w-1/3 flex flex-col">
<FilterSearch
onSelect={handleFilterSelect}
placeholder="Find Locations Near You"
searchFields={[
{
entityType: "location",
fieldApiName: "builtin.location",
},
]}
/>
{/* new code starts here... */}
{resultCount > 0 && <VerticalResults CardComponent={LocationCard} />}
{resultCount === 0 && initialSearchState === "complete" && (
<div className="flex items-center justify-center">
<p className="pt-4 text-2xl">No results found for this area</p>
</div>
)}
</div>
<div className="relative w-2/3">
<MapboxMap
mapboxAccessToken={YEXT_PUBLIC_MAPBOX_API_KEY || ""}
PinComponent={MapPin}
onDrag={handleDrag}
/>
{showSearchAreaButton && (
<div className="absolute top-10 left-0 right-0 flex justify-center">
<button
onClick={handleSearchAreaClick}
className="rounded-2xl border bg-white py-2 px-4 shadow-xl"
>
<p>Search This Area</p>
</button>
</div>
)}
{/* ...and ends here */}
</div>
</div>
</>
);
};
export default StoreLocator;
If your site isn’t already running, start it back up with npm run dev
and go the locator page. After the map loads for the first time, drag your map to the Miami, Florida area. Click on the “Search This Area” button to see your new functionality in action.
onDrag
rather than triggering the search with a button click. You can see an example of how to do this on the
MapboxMap
reference page
.