Skip to content
Open
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
9 changes: 9 additions & 0 deletions docs/api-reference/components/map-control.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,14 @@ const App = () => (
The position is specified as one of the values of the `ControlPosition` enum, which
is an exact copy of the [`google.maps.ControlPosition`][gmp-ctrl-pos] type.

### Optional

#### `className`: string

A CSS class name applied to the container element that wraps the control content
on the map. This is useful for styling or targeting the control from outside,
since the children are rendered into a container that is managed by the Maps
JavaScript API rather than directly into the React tree.

[gmp-custom-ctrl]: https://developers.google.com/maps/documentation/javascript/controls#CustomControls
[gmp-ctrl-pos]: https://developers.google.com/maps/documentation/javascript/controls#ControlPositioning
50 changes: 50 additions & 0 deletions src/components/__tests__/map-control.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,53 @@ test('control is added to the map', () => {
const [controlEl] = (controlsArray.push as jest.Mock).mock.calls[0];
expect(controlEl).toHaveTextContent('control button');
});

test('className prop is applied to the control container', () => {
render(
<MapControl
position={ControlPosition.BOTTOM_CENTER}
className="custom-control">
<button>control button</button>
</MapControl>
);

const controlsArray = mapInstance.controls[ControlPosition.BOTTOM_CENTER];
const [controlEl] = (controlsArray.push as jest.Mock).mock.calls[0];

expect(controlEl).toHaveClass('custom-control');
});

test('className prop updates are reflected on the control container', () => {
const {rerender} = render(
<MapControl
position={ControlPosition.BOTTOM_CENTER}
className="initial-class">
<button>control button</button>
</MapControl>
);

const controlsArray = mapInstance.controls[ControlPosition.BOTTOM_CENTER];
const [controlEl] = (controlsArray.push as jest.Mock).mock.calls[0];

expect(controlEl).toHaveClass('initial-class');

rerender(
<MapControl
position={ControlPosition.BOTTOM_CENTER}
className="updated-class">
<button>control button</button>
</MapControl>
);

expect(controlEl).toHaveClass('updated-class');
expect(controlEl).not.toHaveClass('initial-class');

rerender(
<MapControl position={ControlPosition.BOTTOM_CENTER}>
<button>control button</button>
</MapControl>
);

expect(controlEl).not.toHaveClass('updated-class');
expect(controlEl).not.toHaveClass('initial-class');
});
9 changes: 8 additions & 1 deletion src/components/map-control.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {PropsWithChildren} from 'react';

type MapControlProps = PropsWithChildren<{
position: ControlPosition;
className?: string;
}>;

/**
Expand Down Expand Up @@ -48,11 +49,17 @@ export type ControlPosition =

export const MapControl: FunctionComponent<MapControlProps> = ({
children,
position
position,
className
}) => {
const controlContainer = useMemo(() => document.createElement('div'), []);
const map = useMap();

useEffect(() => {
// eslint-disable-next-line react-hooks/immutability -- the control container DOM node is intentionally mutated from effects
controlContainer.className = className ?? '';
}, [controlContainer, className]);

useEffect(() => {
if (!map) return;

Expand Down