Skip to content

Commit af5ab1c

Browse files
authored
feat: add basic keyboard events for advanced marker (#1001)
1 parent 538e918 commit af5ab1c

3 files changed

Lines changed: 86 additions & 7 deletions

File tree

docs/api-reference/components/advanced-marker.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,9 @@ marker will be clickable and will be interactive for accessibility purposes
203203
By default, this will automatically be set to true when the `onClick` prop
204204
is specified.
205205

206+
It is also automatically set to true when `onKeyDown` or `onKeyUp` is
207+
specified.
208+
206209
#### `draggable`: boolean
207210

208211
Controls if the marker can be repositioned by dragging.
@@ -223,6 +226,14 @@ specified in the position can't be dragged.
223226

224227
This event is fired when the marker is clicked.
225228

229+
#### `onKeyDown`: (e: KeyboardEvent) => void
230+
231+
This event is fired when a key is pressed while the marker is focused.
232+
233+
#### `onKeyUp`: (e: KeyboardEvent) => void
234+
235+
This event is fired when a pressed key is released while the marker is focused.
236+
226237
#### `onMouseEnter`: (e: [google.maps.MapMouseEvent['domEvent']][gmp-map-mouse-ev-dom]) => void
227238

228239
This event is fired when the mouse enters the marker.

src/components/__tests__/advanced-marker.test.tsx

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import React from 'react';
22

33
import {initialize, mockInstances} from '@googlemaps/jest-mocks';
4-
import {cleanup, queryByTestId, render} from '@testing-library/react';
4+
import {
5+
cleanup,
6+
fireEvent,
7+
queryByTestId,
8+
render
9+
} from '@testing-library/react';
510
import '@testing-library/jest-dom';
611

712
import {AdvancedMarker, AdvancedMarkerAnchorPoint} from '../advanced-marker';
@@ -195,8 +200,53 @@ describe('map and marker-library loaded', () => {
195200
expect((marker.content as HTMLElement).style.cursor).toBe('');
196201
});
197202

203+
test('binds gmp-click events to onClick', async () => {
204+
const handleClick = jest.fn();
205+
206+
render(
207+
<AdvancedMarker position={{lat: 1, lng: 2}} onClick={handleClick} />
208+
);
209+
210+
const marker = await waitForMockInstance(
211+
google.maps.marker.AdvancedMarkerElement
212+
);
213+
214+
const clickEvent = new Event('gmp-click');
215+
fireEvent(marker, clickEvent);
216+
217+
expect(handleClick).toHaveBeenCalledWith(clickEvent);
218+
expect(marker.gmpClickable).toBe(true);
219+
});
220+
221+
test('binds keyboard events and infers clickability from them', async () => {
222+
const handleKeyDown = jest.fn();
223+
const handleKeyUp = jest.fn();
224+
225+
render(
226+
<AdvancedMarker
227+
position={{lat: 1, lng: 2}}
228+
onKeyDown={handleKeyDown}
229+
onKeyUp={handleKeyUp}
230+
/>
231+
);
232+
233+
const marker = await waitForMockInstance(
234+
google.maps.marker.AdvancedMarkerElement
235+
);
236+
237+
expect(marker.gmpClickable).toBe(true);
238+
239+
const keyDownEvent = new KeyboardEvent('keydown', {key: 'Enter'});
240+
const keyUpEvent = new KeyboardEvent('keyup', {key: ' '});
241+
242+
fireEvent(marker, keyDownEvent);
243+
fireEvent(marker, keyUpEvent);
244+
245+
expect(handleKeyDown).toHaveBeenCalledWith(keyDownEvent);
246+
expect(handleKeyUp).toHaveBeenCalledWith(keyUpEvent);
247+
});
248+
198249
test.todo('marker should work with options');
199-
test.todo('marker should have a click listener');
200250

201251
describe('anchoring with modern API', () => {
202252
beforeEach(() => {

src/components/advanced-marker.tsx

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ export type AdvancedMarkerAnchorPoint =
7575
(typeof AdvancedMarkerAnchorPoint)[keyof typeof AdvancedMarkerAnchorPoint];
7676

7777
type AdvancedMarkerEventProps = {
78-
onClick?: (e: google.maps.MapMouseEvent) => void;
78+
onClick?: (e: google.maps.marker.AdvancedMarkerClickEvent) => void;
79+
onKeyDown?: (e: KeyboardEvent) => void;
80+
onKeyUp?: (e: KeyboardEvent) => void;
7981
onMouseEnter?: (e: google.maps.MapMouseEvent['domEvent']) => void;
8082
onMouseLeave?: (e: google.maps.MapMouseEvent['domEvent']) => void;
8183
onDrag?: (e: google.maps.MapMouseEvent) => void;
@@ -211,6 +213,8 @@ function useAdvancedMarker(props: AdvancedMarkerProps) {
211213
const {
212214
children,
213215
onClick,
216+
onKeyDown,
217+
onKeyUp,
214218
className,
215219
onMouseEnter,
216220
onMouseLeave,
@@ -296,8 +300,8 @@ function useAdvancedMarker(props: AdvancedMarkerProps) {
296300
else marker.gmpDraggable = false;
297301
}, [marker, draggable, onDrag, onDragEnd, onDragStart]);
298302

299-
// set gmpClickable from props (when unspecified, it's true if the onClick or one of
300-
// the hover events callbacks are specified)
303+
// set gmpClickable from props (when unspecified, it's true if any interactive
304+
// event callbacks are specified)
301305
useEffect(() => {
302306
if (!marker) return;
303307

@@ -306,7 +310,11 @@ function useAdvancedMarker(props: AdvancedMarkerProps) {
306310
const gmpClickable =
307311
clickable !== undefined
308312
? clickable
309-
: Boolean(onClick) || Boolean(onMouseEnter) || Boolean(onMouseLeave);
313+
: Boolean(onClick) ||
314+
Boolean(onKeyDown) ||
315+
Boolean(onKeyUp) ||
316+
Boolean(onMouseEnter) ||
317+
Boolean(onMouseLeave);
310318

311319
// gmpClickable is only available in beta version of the
312320
// maps api (as of 2024-10-10)
@@ -319,13 +327,23 @@ function useAdvancedMarker(props: AdvancedMarkerProps) {
319327
marker.content.style.pointerEvents = gmpClickable ? 'all' : 'none';
320328
marker.content.style.cursor = gmpClickable && onClick ? 'pointer' : '';
321329
}
322-
}, [marker, clickable, onClick, onMouseEnter, onMouseLeave]);
330+
}, [
331+
marker,
332+
clickable,
333+
onClick,
334+
onKeyDown,
335+
onKeyUp,
336+
onMouseEnter,
337+
onMouseLeave
338+
]);
323339

324340
useMapsEventListener(marker, 'drag', onDrag);
325341
useMapsEventListener(marker, 'dragstart', onDragStart);
326342
useMapsEventListener(marker, 'dragend', onDragEnd);
327343

328344
useDomEventListener(marker, 'gmp-click', onClick);
345+
useDomEventListener(marker, 'keydown', onKeyDown);
346+
useDomEventListener(marker, 'keyup', onKeyUp);
329347
useDomEventListener(marker, 'mouseenter', onMouseEnter);
330348
useDomEventListener(marker, 'mouseleave', onMouseLeave);
331349

0 commit comments

Comments
 (0)