Skip to content

Replace pinColor with markerOptionsOverride #522

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
May 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/search-ui-react.mapboxmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ A component that renders a map with markers to show result locations using Mapbo
**Signature:**

```typescript
declare function MapboxMap<T>({ mapboxAccessToken, mapboxOptions, PinComponent, renderPin, getCoordinate, onDrag, iframeWindow, allowUpdates, scrollToResult, pinColor, }: MapboxMapProps<T>): JSX.Element;
declare function MapboxMap<T>({ mapboxAccessToken, mapboxOptions, PinComponent, renderPin, getCoordinate, onDrag, iframeWindow, allowUpdates, scrollToResult, markerOptionsOverride, }: MapboxMapProps<T>): JSX.Element;
```

## Parameters
Expand All @@ -32,7 +32,7 @@ Description
</th></tr></thead>
<tbody><tr><td>

{ mapboxAccessToken, mapboxOptions, PinComponent, renderPin, getCoordinate, onDrag, iframeWindow, allowUpdates, scrollToResult, pinColor, }
{ mapboxAccessToken, mapboxOptions, PinComponent, renderPin, getCoordinate, onDrag, iframeWindow, allowUpdates, scrollToResult, markerOptionsOverride, }


</td><td>
Expand Down
13 changes: 13 additions & 0 deletions docs/search-ui-react.mapboxmapprops.markeroptionsoverride.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@yext/search-ui-react](./search-ui-react.md) &gt; [MapboxMapProps](./search-ui-react.mapboxmapprops.md) &gt; [markerOptionsOverride](./search-ui-react.mapboxmapprops.markeroptionsoverride.md)

## MapboxMapProps.markerOptionsOverride property

The options to apply to the map markers based on whether it is selected. By default, the standard Mapbox pin is used. This prop should not be used with [PinComponent](./search-ui-react.mapboxmapprops.pincomponent.md) or with [renderPin](./search-ui-react.mapboxmapprops.renderpin.md)<!-- -->. If either are provided, markerOptionsOverride will be ignored.

**Signature:**

```typescript
markerOptionsOverride?: (selected: boolean) => MarkerOptions;
```
12 changes: 6 additions & 6 deletions docs/search-ui-react.mapboxmapprops.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,39 +132,39 @@ _(Optional)_ Interface for map customization derived from Mapbox GL's Map option
</td></tr>
<tr><td>

[onDrag?](./search-ui-react.mapboxmapprops.ondrag.md)
[markerOptionsOverride?](./search-ui-react.mapboxmapprops.markeroptionsoverride.md)


</td><td>


</td><td>

[OnDragHandler](./search-ui-react.ondraghandler.md)
(selected: boolean) =&gt; MarkerOptions


</td><td>

_(Optional)_ A function which is called when user drags or zooms the map.
_(Optional)_ The options to apply to the map markers based on whether it is selected. By default, the standard Mapbox pin is used. This prop should not be used with [PinComponent](./search-ui-react.mapboxmapprops.pincomponent.md) or with [renderPin](./search-ui-react.mapboxmapprops.renderpin.md)<!-- -->. If either are provided, markerOptionsOverride will be ignored.


</td></tr>
<tr><td>

[pinColor?](./search-ui-react.mapboxmapprops.pincolor.md)
[onDrag?](./search-ui-react.mapboxmapprops.ondrag.md)


</td><td>


</td><td>

string
[OnDragHandler](./search-ui-react.ondraghandler.md)


</td><td>

_(Optional)_ The color that default map markers should be, in the form of a HEX color code. By default, the standard Mapbox pin color is used, which is light blue (\#3FB1CE). This prop should not be used with [PinComponent](./search-ui-react.mapboxmapprops.pincomponent.md) or with [renderPin](./search-ui-react.mapboxmapprops.renderpin.md)<!-- -->. If either are provided, pinColor will be ignored.
_(Optional)_ A function which is called when user drags or zooms the map.


</td></tr>
Expand Down
13 changes: 0 additions & 13 deletions docs/search-ui-react.mapboxmapprops.pincolor.md

This file was deleted.

2 changes: 1 addition & 1 deletion docs/search-ui-react.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ A React Component which displays and collects location information in order to b
</td></tr>
<tr><td>

[MapboxMap({ mapboxAccessToken, mapboxOptions, PinComponent, renderPin, getCoordinate, onDrag, iframeWindow, allowUpdates, scrollToResult, pinColor, })](./search-ui-react.mapboxmap.md)
[MapboxMap({ mapboxAccessToken, mapboxOptions, PinComponent, renderPin, getCoordinate, onDrag, iframeWindow, allowUpdates, scrollToResult, markerOptionsOverride, })](./search-ui-react.mapboxmap.md)


</td><td>
Expand Down
5 changes: 3 additions & 2 deletions etc/search-ui-react.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { FilterSearchResponse } from '@yext/search-headless-react';
import { GenerativeDirectAnswerResponse } from '@yext/search-headless-react';
import { HighlightedValue } from '@yext/search-headless-react';
import { default as mapboxgl_2 } from 'mapbox-gl';
import { MarkerOptions } from 'mapbox-gl';
import { Matcher } from '@yext/search-headless-react';
import { NumberRangeValue } from '@yext/search-headless-react';
import { PropsWithChildren } from 'react';
Expand Down Expand Up @@ -490,7 +491,7 @@ export interface LocationBiasProps {
}

// @public
export function MapboxMap<T>({ mapboxAccessToken, mapboxOptions, PinComponent, renderPin, getCoordinate, onDrag, iframeWindow, allowUpdates, scrollToResult, pinColor, }: MapboxMapProps<T>): JSX.Element;
export function MapboxMap<T>({ mapboxAccessToken, mapboxOptions, PinComponent, renderPin, getCoordinate, onDrag, iframeWindow, allowUpdates, scrollToResult, markerOptionsOverride, }: MapboxMapProps<T>): JSX.Element;

// @public
export interface MapboxMapProps<T> {
Expand All @@ -499,8 +500,8 @@ export interface MapboxMapProps<T> {
iframeWindow?: Window;
mapboxAccessToken: string;
mapboxOptions?: Omit<mapboxgl_2.MapboxOptions, 'container'>;
markerOptionsOverride?: (selected: boolean) => MarkerOptions;
onDrag?: OnDragHandler;
pinColor?: string;
PinComponent?: PinComponent<T>;
renderPin?: (props: PinComponentProps<T> & {
container: HTMLElement;
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@yext/search-ui-react",
"version": "1.8.7",
"version": "1.8.8",
"description": "A library of React Components for powering Yext Search integrations",
"author": "watson@yext.com",
"license": "BSD-3-Clause",
Expand Down
28 changes: 18 additions & 10 deletions src/components/MapboxMap.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useRef, useEffect, useState, useCallback } from 'react';
import mapboxgl from 'mapbox-gl';
import mapboxgl, { MarkerOptions } from 'mapbox-gl';
import { Result, useSearchState } from '@yext/search-headless-react';
import { useDebouncedFunction } from '../hooks/useDebouncedFunction';
import ReactDOM from 'react-dom';
Expand Down Expand Up @@ -104,13 +104,13 @@ export interface MapboxMapProps<T> {
/** A function that scrolls to the search result corresponding to the selected pin. */
scrollToResult?: (result: Result<T> | undefined) => void,
/**
* The color that default map markers should be, in the form of a HEX color code.
* By default, the standard Mapbox pin color is used, which is light blue (#3FB1CE).
* The options to apply to the map markers based on whether it is selected.
* By default, the standard Mapbox pin is used.
* This prop should not be used with {@link MapboxMapProps.PinComponent | PinComponent}
* or with {@link MapboxMapProps.renderPin | renderPin}. If either are provided,
* pinColor will be ignored.
* markerOptionsOverride will be ignored.
*/
pinColor?: string
markerOptionsOverride?: (selected: boolean) => MarkerOptions,
}

/**
Expand Down Expand Up @@ -142,7 +142,7 @@ export function MapboxMap<T>({
iframeWindow,
allowUpdates = false,
scrollToResult,
pinColor,
markerOptionsOverride,
}: MapboxMapProps<T>): JSX.Element {
const mapboxInstance = (iframeWindow as Window & { mapboxgl?: typeof mapboxgl })?.mapboxgl ?? mapboxgl;
useEffect(() => {
Expand Down Expand Up @@ -219,7 +219,7 @@ export function MapboxMap<T>({
if (markerLocation) {
const { latitude, longitude } = markerLocation;
const el = document.createElement('div');
const markerOptions: mapboxgl.MarkerOptions = {};
let markerOptions: mapboxgl.MarkerOptions = {};
if (PinComponent) {
if (renderPin) {
console.warn(
Expand All @@ -237,12 +237,20 @@ export function MapboxMap<T>({
} else if (renderPin) {
renderPin({ index: i, mapbox, result, container: el });
markerOptions.element = el;
} else if (pinColor) {
markerOptions.color = pinColor;
} else if (markerOptionsOverride) {
markerOptions = {
...markerOptions,
...markerOptionsOverride(selectedResult === result)
}
}
const marker = new mapboxInstance.Marker(markerOptions)
.setLngLat({ lat: latitude, lng: longitude })
.addTo(mapbox);

if (!PinComponent) {
marker?.getElement().addEventListener('click', () => handlePinClick(result));
}

markers.current.push(marker);
bounds.extend([longitude, latitude]);
}
Expand All @@ -255,7 +263,7 @@ export function MapboxMap<T>({
});
}
}
}, [PinComponent, getCoordinate, locationResults, selectedResult, pinColor]);
}, [PinComponent, getCoordinate, locationResults, selectedResult, markerOptionsOverride]);

return (
<div ref={mapContainer} className='h-full w-full' />
Expand Down
2 changes: 1 addition & 1 deletion test-site/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 60 additions & 4 deletions test-site/src/pages/LocationsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useSearchActions, Matcher, useSearchState, SelectableStaticFilter } from '@yext/search-headless-react';
import { useSearchActions, Matcher, useSearchState, SelectableStaticFilter, Result } from '@yext/search-headless-react';
import {
AppliedFilters,
ResultsCount,
Expand All @@ -13,25 +13,33 @@ import {
Coordinate,
} from '@yext/search-ui-react';
import { LngLat, LngLatBounds } from 'mapbox-gl';
import { useCallback, useLayoutEffect } from 'react';
import { useCallback, useLayoutEffect, useRef } from 'react';
import { MapPin } from '../components/MapPin';

export interface Location {
yextDisplayCoordinate?: Coordinate
}

const mapboxOptions: MapboxMapProps<Location>['mapboxOptions'] = {
const mapboxOptions: MapboxMapProps<Record<string, unknown>>['mapboxOptions'] = {
zoom: 10
};

export function LocationsPage() {
const searchActions = useSearchActions();
const filters = useSearchState(state => state.filters.static);
useLayoutEffect(() => {
searchActions.setVertical('KM');
searchActions.setVertical('locations');
searchActions.executeVerticalQuery();
}, [searchActions]);

const resultsRef = useRef<Array<HTMLDivElement | null>>([]);
const resultsContainer = useRef<HTMLDivElement>(null);

const setResultsRef = useCallback((index: number) => {
if (!resultsRef?.current) return null;
return (result: HTMLDivElement) => (resultsRef.current[index] = result);
}, []);

const onDrag: OnDragHandler = useCallback(
(center: LngLat, bounds: LngLatBounds) => {
const radius = center.distanceTo(bounds.getNorthEast());
Expand All @@ -49,6 +57,27 @@ export function LocationsPage() {
searchActions.setStaticFilters([...nonLocationFilters, nearFilter]);
searchActions.executeVerticalQuery();
}, [filters, searchActions]);

const scrollToResult = useCallback((result: Result | undefined) => {
if (result) {
const scrollTop = resultsRef.current
.filter((r, index) => r && result.index ? index < result.index : false)
.map((elem) => elem?.scrollHeight ?? 0)
.reduce((total, height) => total + height + 16);
resultsContainer.current?.scroll({
top: scrollTop,
behavior: "smooth",
});
}
}, [resultsRef.current, resultsContainer])

const markerOptionsOverride = useCallback((selected: boolean) => {
return {
color: '#FFB6C1',
scale: selected ? 1.5 : 1,
}
}, [])

return (
<div>
<SearchBar />
Expand All @@ -73,4 +102,31 @@ export function LocationsPage() {
</div>
</div>
);

/**
* uncomment this to test side by side view of search results and interactive map pins
* note that scroll to results do not fully work ONLY on this test-site due to extra height in the StandardCard padding
*/
// return (
// <div className="flex h-96">
// <div ref={resultsContainer} className="w-1/4 p-4 overflow-y-auto">
// <SearchBar />
// <ResultsCount />
// <AppliedFilters />
// <VerticalResults
// setResultsRef={setResultsRef}
// CardComponent={StandardCard}
// />
// </div>
// <div className="w-3/4 p-4">
// <MapboxMap
// mapboxAccessToken={process.env.REACT_APP_MAPBOX_API_KEY || 'REPLACE_KEY'}
// mapboxOptions={mapboxOptions}
// onDrag={onDrag}
// scrollToResult={scrollToResult}
// markerOptionsOverride={markerOptionsOverride}
// />
// </div>
// </div>
// )
}
Loading