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
48 changes: 48 additions & 0 deletions src/components/__tests__/advanced-marker.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,54 @@ describe('map and marker-library loaded', () => {
(google.maps as any).version = '3.62.9';
});

// Regression test for issue #867: when the consumer provides only
// `anchorTop` (or only `anchorLeft`), the library must not assign
// `undefined` to the unspecified side. Doing so clobbers the
// default value the Google Maps JS API sets up, and combined with
// `gmpDraggable = true` the API subsequently throws
// `TypeError: Failed to execute 'appendChild' on 'Node'` while
// wiring up the drag handle.
test('only the provided anchor prop is assigned, the other side is left untouched (#867)', async () => {
// The mock AdvancedMarkerElement is a plain class extending the
// jest-mocks HTMLElement; it does not pre-define `anchorLeft` /
// `anchorTop` on instances. Therefore, when the library assigns
// `undefined` to the unprovided side, an own property with
// value `undefined` is created. After the fix, that own property
// must NOT exist because the library must not touch the side the
// consumer did not provide.
const {unmount} = render(
<AdvancedMarker position={{lat: 0, lng: 0}} anchorTop={'-90%'}>
<div />
</AdvancedMarker>
);

const marker = await waitForMockInstance(
google.maps.marker.AdvancedMarkerElement
);
expect(marker.anchorTop).toBe('-90%');
// After the fix: anchorLeft must remain unset (no own property).
expect(
Object.getOwnPropertyDescriptor(marker, 'anchorLeft')
).toBeUndefined();

unmount();

render(
<AdvancedMarker position={{lat: 0, lng: 0}} anchorLeft={'10px'}>
<div />
</AdvancedMarker>
);

const marker2 = await waitForMockInstance(
google.maps.marker.AdvancedMarkerElement
);
expect(marker2.anchorLeft).toBe('10px');
// After the fix: anchorTop must remain unset (no own property).
expect(
Object.getOwnPropertyDescriptor(marker2, 'anchorTop')
).toBeUndefined();
});

test('anchorLeft/anchorTop should have precedence over anchorPoint', async () => {
const consoleWarnSpy = jest
.spyOn(console, 'warn')
Expand Down
10 changes: 8 additions & 2 deletions src/components/advanced-marker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,14 @@ function useAdvancedMarkerAnchoring(
);
}

marker.anchorLeft = anchorLeft;
marker.anchorTop = anchorTop;
// Only assign properties that were actually provided by the consumer.
// Assigning `undefined` to `anchorLeft`/`anchorTop` would clobber the
// element's default value, and the Google Maps JS API can subsequently
// fail with a `TypeError: Failed to execute 'appendChild' on 'Node'`
// when it later reads these properties while setting up drag handles
// (see issue #867).
if (anchorLeft !== undefined) marker.anchorLeft = anchorLeft;
if (anchorTop !== undefined) marker.anchorTop = anchorTop;

// when anchorLeft and/or anchorTop are set, we'll ignore the anchorPoint
if (anchorPoint !== undefined) {
Expand Down