Skip to content

Commit

Permalink
Switch to providing renderPin prop
Browse files Browse the repository at this point in the history
  • Loading branch information
nmanu1 committed Feb 16, 2024
1 parent 58d7384 commit e22f1c5
Show file tree
Hide file tree
Showing 16 changed files with 135 additions and 27 deletions.
4 changes: 1 addition & 3 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { StorybookConfig } from '@storybook/react-webpack5';
import React from 'react';

const config: StorybookConfig = {
stories: [
Expand Down Expand Up @@ -40,8 +39,7 @@ const config: StorybookConfig = {
'./SearchCore': require.resolve('../tests/__fixtures__/core/SearchCore.ts'),
'../utils/location-operations': require.resolve('../tests/__fixtures__/utils/location-operations.ts')
},
},
...(!React.version.startsWith('18') && { externals: ["react-dom/client"] })
}
}),

env: (config) => {
Expand Down
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,14 +9,14 @@ A component that renders a map with markers to show result locations using Mapbo
**Signature:**

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

## Parameters

| Parameter | Type | Description |
| --- | --- | --- |
| { mapboxAccessToken, mapboxOptions, PinComponent, getCoordinate, onDrag } | [MapboxMapProps](./search-ui-react.mapboxmapprops.md)<!-- -->&lt;T&gt; | |
| { mapboxAccessToken, mapboxOptions, PinComponent, renderPin, getCoordinate, onDrag } | [MapboxMapProps](./search-ui-react.mapboxmapprops.md)<!-- -->&lt;T&gt; | |

**Returns:**

Expand Down
3 changes: 2 additions & 1 deletion docs/search-ui-react.mapboxmapprops.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ interface MapboxMapProps<T>
| [mapboxAccessToken](./search-ui-react.mapboxmapprops.mapboxaccesstoken.md) | | string | Mapbox access token. |
| [mapboxOptions?](./search-ui-react.mapboxmapprops.mapboxoptions.md) | | Omit&lt;mapboxgl.MapboxOptions, 'container'&gt; | _(Optional)_ Interface for map customization derived from Mapbox GL's Map options. |
| [onDrag?](./search-ui-react.mapboxmapprops.ondrag.md) | | [OnDragHandler](./search-ui-react.ondraghandler.md) | _(Optional)_ A function which is called when user drag the map. |
| [PinComponent?](./search-ui-react.mapboxmapprops.pincomponent.md) | | [PinComponent](./search-ui-react.pincomponent.md)<!-- -->&lt;T&gt; | _(Optional)_ Custom Pin component to render for markers on the map. By default, the built-in marker image from Mapbox GL is used. |
| [PinComponent?](./search-ui-react.mapboxmapprops.pincomponent.md) | | [PinComponent](./search-ui-react.pincomponent.md)<!-- -->&lt;T&gt; | _(Optional)_ Custom Pin component to render for markers on the map. By default, the built-in marker image from Mapbox GL is used. This prop should not be used with [renderPin](./search-ui-react.mapboxmapprops.renderpin.md)<!-- -->. If both are provided, only PinComponent will be used. |
| [renderPin?](./search-ui-react.mapboxmapprops.renderpin.md) | | (props: [PinComponentProps](./search-ui-react.pincomponentprops.md)<!-- -->&lt;T&gt; &amp; { container: HTMLElement; }) =&gt; void | _(Optional)_ Render function for a custom marker on the map. This function takes in an HTML element and is responible for rendering the pin into that element, which will be used as the marker. By default, the built-in marker image from Mapbox GL is used. This prop should not be used with [PinComponent](./search-ui-react.mapboxmapprops.pincomponent.md)<!-- -->. If both are provided, only PinComponent will be used. |

2 changes: 1 addition & 1 deletion docs/search-ui-react.mapboxmapprops.pincomponent.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## MapboxMapProps.PinComponent property

Custom Pin component to render for markers on the map. By default, the built-in marker image from Mapbox GL is used.
Custom Pin component to render for markers on the map. By default, the built-in marker image from Mapbox GL is used. This prop should not be used with [renderPin](./search-ui-react.mapboxmapprops.renderpin.md)<!-- -->. If both are provided, only PinComponent will be used.

**Signature:**

Expand Down
15 changes: 15 additions & 0 deletions docs/search-ui-react.mapboxmapprops.renderpin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!-- 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; [renderPin](./search-ui-react.mapboxmapprops.renderpin.md)

## MapboxMapProps.renderPin property

Render function for a custom marker on the map. This function takes in an HTML element and is responible for rendering the pin into that element, which will be used as the marker. By default, the built-in marker image from Mapbox GL is used. This prop should not be used with [PinComponent](./search-ui-react.mapboxmapprops.pincomponent.md)<!-- -->. If both are provided, only PinComponent will be used.

**Signature:**

```typescript
renderPin?: (props: PinComponentProps<T> & {
container: HTMLElement;
}) => void;
```
3 changes: 2 additions & 1 deletion docs/search-ui-react.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
| [HierarchicalFacets({ searchOnChange, collapsible, defaultExpanded, includedFieldIds, customCssClasses, delimiter, showMoreLimit })](./search-ui-react.hierarchicalfacets.md) | A component that displays hierarchical facets, in a tree level structure, applicable to the current vertical search. |
| [isCtaData(data)](./search-ui-react.isctadata.md) | Type guard for CtaData. |
| [LocationBias({ geolocationOptions, customCssClasses })](./search-ui-react.locationbias.md) | A React Component which displays and collects location information in order to bias searches. |
| [MapboxMap({ mapboxAccessToken, mapboxOptions, PinComponent, getCoordinate, onDrag })](./search-ui-react.mapboxmap.md) | A component that renders a map with markers to show result locations using Mapbox GL. |
| [MapboxMap({ mapboxAccessToken, mapboxOptions, PinComponent, renderPin, getCoordinate, onDrag })](./search-ui-react.mapboxmap.md) | A component that renders a map with markers to show result locations using Mapbox GL. |
| [NumericalFacet(props)](./search-ui-react.numericalfacet.md) | A component that displays a single numerical facet. Use this to override the default rendering. |
| [NumericalFacets({ searchOnChange, includedFieldIds, getFilterDisplayName, inputPrefix, customCssClasses, ...filterGroupProps })](./search-ui-react.numericalfacets.md) | A component that displays numerical facets applicable to the current vertical search. |
| [Pagination(props)](./search-ui-react.pagination.md) | Renders a component that divide a series of vertical results into chunks across multiple pages and enable user to navigate between those pages. |
Expand Down Expand Up @@ -142,6 +142,7 @@
| [OnDragHandler](./search-ui-react.ondraghandler.md) | A function which is called when user drag the map. |
| [onSearchFunc](./search-ui-react.onsearchfunc.md) | The interface of a function which is called on a search. |
| [PinComponent](./search-ui-react.pincomponent.md) | A functional component that can be used to render a custom marker on the map. |
| [PinComponentProps](./search-ui-react.pincomponentprops.md) | Props for rendering a custom marker on the map. |
| [RenderEntityPreviews](./search-ui-react.renderentitypreviews.md) | The type of a functional React component which renders entity previews using a map of vertical key to the corresponding VerticalResults data. |
| [SectionComponent](./search-ui-react.sectioncomponent.md) | A component that can be used to render a section template for vertical results. |
| [StaticFilterOptionConfig](./search-ui-react.staticfilteroptionconfig.md) | The configuration data for a field value static filter option. |
Expand Down
8 changes: 3 additions & 5 deletions docs/search-ui-react.pincomponent.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ A functional component that can be used to render a custom marker on the map.
**Signature:**

```typescript
type PinComponent<T> = (props: {
index: number;
mapbox: mapboxgl.Map;
result: Result<T>;
}) => JSX.Element;
type PinComponent<T> = (props: PinComponentProps<T>) => JSX.Element;
```
**References:** [PinComponentProps](./search-ui-react.pincomponentprops.md)

17 changes: 17 additions & 0 deletions docs/search-ui-react.pincomponentprops.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!-- 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; [PinComponentProps](./search-ui-react.pincomponentprops.md)

## PinComponentProps type

Props for rendering a custom marker on the map.

**Signature:**

```typescript
type PinComponentProps<T> = {
index: number;
mapbox: mapboxgl.Map;
result: Result<T>;
};
```
12 changes: 9 additions & 3 deletions etc/search-ui-react.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ export interface LocationBiasProps {
}

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

// @public
export interface MapboxMapProps<T> {
Expand All @@ -427,6 +427,9 @@ export interface MapboxMapProps<T> {
mapboxOptions?: Omit<mapboxgl_2.MapboxOptions, 'container'>;
onDrag?: OnDragHandler;
PinComponent?: PinComponent<T>;
renderPin?: (props: PinComponentProps<T> & {
container: HTMLElement;
}) => void;
}

// @public
Expand Down Expand Up @@ -511,11 +514,14 @@ export interface PaginationProps {
}

// @public
export type PinComponent<T> = (props: {
export type PinComponent<T> = (props: PinComponentProps<T>) => JSX.Element;

// @public
export type PinComponentProps<T> = {
index: number;
mapbox: mapboxgl_2.Map;
result: Result<T>;
}) => JSX.Element;
};

// @public
export interface RangeInputCssClasses {
Expand Down
42 changes: 37 additions & 5 deletions src/components/MapboxMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,28 @@ import React, { useRef, useEffect } from 'react';
import mapboxgl from 'mapbox-gl';
import { Result, useSearchState } from '@yext/search-headless-react';
import { useDebouncedFunction } from '../hooks/useDebouncedFunction';
import { renderReact } from './utils/renderReact';
import ReactDOM from 'react-dom';

/**
* A functional component that can be used to render a custom marker on the map.
* Props for rendering a custom marker on the map.
*
* @public
*/
export type PinComponent<T> = (props: {
export type PinComponentProps<T> = {
/** The index of the pin. */
index: number,
/** The Mapbox map. */
mapbox: mapboxgl.Map,
/** The search result corresponding to the pin. */
result: Result<T>
}) => JSX.Element;
};

/**
* A functional component that can be used to render a custom marker on the map.
*
* @public
*/
export type PinComponent<T> = (props: PinComponentProps<T>) => JSX.Element;

/**
* A function use to derive a result's coordinate.
Expand Down Expand Up @@ -55,8 +65,21 @@ export interface MapboxMapProps<T> {
/**
* Custom Pin component to render for markers on the map.
* By default, the built-in marker image from Mapbox GL is used.
* This prop should not be used with
* {@link MapboxMapProps.renderPin | renderPin}. If both are provided,
* only PinComponent will be used.
*/
PinComponent?: PinComponent<T>,
/**
* Render function for a custom marker on the map. This function takes in an
* HTML element and is responible for rendering the pin into that element,
* which will be used as the marker.
* By default, the built-in marker image from Mapbox GL is used.
* This prop should not be used with
* {@link MapboxMapProps.PinComponent | PinComponent}. If both are provided,
* only PinComponent will be used.
*/
renderPin?: (props: PinComponentProps<T> & { container: HTMLElement }) => void,
/**
* A function to derive a result's coordinate for the corresponding marker's location on the map.
* By default, "yextDisplayCoordinate" field is used as the result's display coordinate.
Expand Down Expand Up @@ -89,6 +112,7 @@ export function MapboxMap<T>({
mapboxAccessToken,
mapboxOptions,
PinComponent,
renderPin,
getCoordinate = getDefaultCoordinate,
onDrag
}: MapboxMapProps<T>): JSX.Element {
Expand Down Expand Up @@ -136,12 +160,20 @@ export function MapboxMap<T>({
const el = document.createElement('div');
const markerOptions: mapboxgl.MarkerOptions = {};
if (PinComponent) {
renderReact(<PinComponent
if (renderPin) {
console.warn(
'Found both PinComponent and renderPin props. Using PinComponent.'
);
}
ReactDOM.render(<PinComponent
index={i}
mapbox={mapbox}
result={result}
/>, el);
markerOptions.element = el;
} else if (renderPin) {
renderPin({ index: i, mapbox, result, container: el });
markerOptions.element = el;
}
const marker = new mapboxgl.Marker(markerOptions)
.setLngLat({ lat: latitude, lng: longitude })
Expand Down
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ export {
export {
MapboxMap,
PinComponent,
PinComponentProps,
MapboxMapProps,
OnDragHandler,
CoordinateGetter,
Expand Down
8 changes: 4 additions & 4 deletions test-site/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import 'mapbox-gl/dist/mapbox-gl.css';
import App from './App';
import { renderReact } from './utils/renderReact';

const container = document.getElementById('root');
const root = createRoot(container!);
root.render(
renderReact(
<StrictMode>
<App/>
</StrictMode>
</StrictMode>,
container!
);
File renamed without changes.
19 changes: 19 additions & 0 deletions tests/components/MapboxMap.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { generateMockedHeadless } from '../__fixtures__/search-headless';
import { MapboxMap, MapboxMapProps } from '../../src/components/MapboxMap';
import { MapPin } from '../../test-site/src/components/MapPin';
import { Location } from '../../test-site/src/pages/LocationsPage';
import { renderReact } from '../../test-site/src/utils/renderReact';
import { locationVerticalSingle, locationVerticalMultiple } from '../__fixtures__/data/mapbox';
import React from 'react';

Expand All @@ -20,6 +21,9 @@ const meta: Meta<typeof MapboxMap> = {
PinComponent: {
control: false,
},
renderPin: {
control: false,
},
},
args: {
mapboxAccessToken: process.env.REACT_APP_MAPBOX_API_KEY,
Expand Down Expand Up @@ -59,3 +63,18 @@ CustomPin.play = async ({ canvasElement }) => {
fireEvent.click(mapPin);
await canvas.findByText('title1');
};

export const CustomRenderPin: StoryFn<MapboxMapProps<Location>> = Template.bind({});

CustomRenderPin.args = {
renderPin: (props) => renderReact(<MapPin {...props} />, props.container),
};

CustomRenderPin.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);
const mapPin = await canvas.findByLabelText('Show pin details', undefined, {
timeout: 30000
});
fireEvent.click(mapPin);
await canvas.findByText('title1');
};
21 changes: 21 additions & 0 deletions tests/components/MapboxMap.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,24 @@ it('registers "onDrag" callback to Mapbox\'s event listener for "drag to pan" in
jest.advanceTimersByTime(100); //debounce time
expect(onDragFn).toBeCalledTimes(1);
});

it('uses PinComponent and logs warning if both PinComponent and renderPin are provided', () => {
mockAnswersState(mockedStateDefaultCoordinate);
jest.spyOn(Marker.prototype, 'setLngLat').mockReturnValue(Marker.prototype);
const PinComponent = jest.fn();
const renderPin = jest.fn();
const warnSpy = jest.spyOn(console, 'warn').mockImplementation();

render(<MapboxMap
mapboxAccessToken='TEST_KEY'
PinComponent={PinComponent}
renderPin={renderPin}
/>);

expect(warnSpy).toBeCalledTimes(1);
expect(warnSpy).toBeCalledWith(
'Found both PinComponent and renderPin props. Using PinComponent.'
);
expect(PinComponent).toBeCalledTimes(1);
expect(renderPin).not.toBeCalled();
});
3 changes: 1 addition & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
"declarationMap": true,
"sourceMap": true,
"jsx": "react",
"target": "es2022",
"module": "es2022"
"target": "es2017"
},
"include": [
"src"
Expand Down

0 comments on commit e22f1c5

Please sign in to comment.