From bc8b7bd8531678914babb8e00cc9ca8b0abcc3d5 Mon Sep 17 00:00:00 2001 From: Dan Haas Date: Tue, 25 Mar 2025 11:58:12 -0400 Subject: [PATCH 1/3] refactors modal wrapper event handling --- src/components/modal/v2/lib/zoid-polyfill.js | 47 +++++++++++++------ .../modal/v2/lib/zoid-polyfill.test.js | 45 +++++++++++++++--- 2 files changed, 71 insertions(+), 21 deletions(-) diff --git a/src/components/modal/v2/lib/zoid-polyfill.js b/src/components/modal/v2/lib/zoid-polyfill.js index 48a76ea0a5..68f0d79e72 100644 --- a/src/components/modal/v2/lib/zoid-polyfill.js +++ b/src/components/modal/v2/lib/zoid-polyfill.js @@ -12,6 +12,7 @@ export const POSTMESSENGER_EVENT_NAMES = { CLOSE: 'paypal-messages-modal-close', SHOW: 'paypal-messages-modal-show' }; +export const WRAPPER_CLOSE_MESSAGE_NAME = 'MODAL_CLOSED'; function listenAndAssignProps(newProps, propListeners) { Array.from(propListeners.values()).forEach(listener => { @@ -20,20 +21,43 @@ function listenAndAssignProps(newProps, propListeners) { Object.assign(window.xprops, newProps); } -export function validateAndUpdateBrowserProps(clientOrigin, propListeners, updatedPropsEvent) { +export function validateAndUpdateBrowserProps(propListeners, updatedPropsEvent) { const { - origin: eventOrigin, - data: { eventName, id, eventPayload: newProps } + data: { eventPayload: newProps } } = updatedPropsEvent; - - if (eventOrigin === clientOrigin && eventName === 'PROPS_UPDATE' && newProps && typeof newProps === 'object') { - // send event ack with original event id so PostMessenger will stop reposting event - sendEvent(new PostMessengerMessage('ack', id)); + if (newProps && typeof newProps === 'object') { const validProps = validateProps(newProps); listenAndAssignProps(validProps, propListeners); } } +export function logModalClose(linkName) { + logger.track({ + index: '1', + et: 'CLICK', + event_type: 'modal_close', + page_view_link_name: linkName + }); +} + +export function handleModalWrapperEvents(clientOrigin, propListeners, event) { + const { + origin: eventOrigin, + data: { eventName, id } + } = event; + if (eventOrigin !== clientOrigin) { + return; + } + if (eventName === 'PROPS_UPDATE') { + validateAndUpdateBrowserProps(propListeners, event); + } + if (eventName === WRAPPER_CLOSE_MESSAGE_NAME) { + logModalClose(event.data.eventPayload.linkName); + } + // send event ack with original event id so PostMessenger will stop reposting event + sendEvent(new PostMessengerMessage('ack', id)); +} + const setupBrowser = props => { const propListeners = new Set(); const clientOrigin = decodeURIComponent(props.origin); @@ -41,7 +65,7 @@ const setupBrowser = props => { window.addEventListener( 'message', event => { - validateAndUpdateBrowserProps(clientOrigin, propListeners, event); + handleModalWrapperEvents(clientOrigin, propListeners, event); }, false ); @@ -133,12 +157,7 @@ const setupBrowser = props => { linkName }; sendEvent(new PostMessengerMessage('message', POSTMESSENGER_EVENT_NAMES.CLOSE, eventPayload), clientOrigin); - logger.track({ - index: '1', - et: 'CLICK', - event_type: 'modal_close', - page_view_link_name: linkName - }); + logModalClose(linkName); }, // Overridable defaults integrationType: __MESSAGES__.__TARGET__, diff --git a/tests/unit/spec/src/components/modal/v2/lib/zoid-polyfill.test.js b/tests/unit/spec/src/components/modal/v2/lib/zoid-polyfill.test.js index 0f26f2d76d..64902c5f41 100644 --- a/tests/unit/spec/src/components/modal/v2/lib/zoid-polyfill.test.js +++ b/tests/unit/spec/src/components/modal/v2/lib/zoid-polyfill.test.js @@ -1,6 +1,7 @@ import zoidPolyfill, { - validateAndUpdateBrowserProps, - POSTMESSENGER_EVENT_NAMES + handleModalWrapperEvents, + POSTMESSENGER_EVENT_NAMES, + WRAPPER_CLOSE_MESSAGE_NAME } from 'src/components/modal/v2/lib/zoid-polyfill'; import { logger } from 'src/utils'; @@ -450,9 +451,9 @@ describe('zoidPollyfill', () => { expect(addEventListenerSpy).toHaveBeenCalledTimes(1); expect(addEventListenerSpy).toHaveBeenCalledWith('message', expect.any(Function), false); }); - test('validateAndUpdateBrowserProps updates props when values are valid', () => { + test('handleModalWrapperEvents handles PROPS_UPDATE and updates props when values are valid', () => { // jest doesn't support calling postMessage, so we cannot use the event listener above - // instead we will manually verify that validateAndUpdateBrowserProps works as intended + // instead we will manually verify that handleModalWrapperEvents works as intended const clientOrigin = 'http://example.com'; const newPropsEvent = { @@ -469,7 +470,7 @@ describe('zoidPollyfill', () => { const propListeners = new Set(); const onPropsCallback = jest.fn(); propListeners.add(onPropsCallback); - validateAndUpdateBrowserProps(clientOrigin, propListeners, newPropsEvent); + handleModalWrapperEvents(clientOrigin, propListeners, newPropsEvent); expect(onPropsCallback).toHaveBeenCalledTimes(1); expect(onPropsCallback).toHaveBeenCalledWith( @@ -481,7 +482,37 @@ describe('zoidPollyfill', () => { }) ); }); - test('validateAndUpdateBrowserProps handles unrelated events with no data', () => { + test('handleModalWrapperEvents handles MODAL_CLOSE and logs close method', () => { + // jest doesn't support calling postMessage, so we cannot use the event listener above + // instead we will manually verify that handleModalWrapperEvents works as intended + const clientOrigin = 'http://example.com'; + + const newPropsEvent = { + origin: clientOrigin, + data: { + eventName: WRAPPER_CLOSE_MESSAGE_NAME, + eventPayload: { + linkName: 'Custom Close Button' + } + } + }; + + const propListeners = new Set(); + const onPropsCallback = jest.fn(); + propListeners.add(onPropsCallback); + handleModalWrapperEvents(clientOrigin, propListeners, newPropsEvent); + + expect(logger.track).toHaveBeenCalledTimes(1); + expect(logger.track).toHaveBeenCalledWith( + expect.objectContaining({ + index: '1', + et: 'CLICK', + event_type: 'modal_close', + page_view_link_name: 'Custom Close Button' + }) + ); + }); + test('handleModalWrapperEvents handles unrelated events with no data', () => { const unrelatedEvent = { data: {} }; @@ -489,7 +520,7 @@ describe('zoidPollyfill', () => { const propListeners = new Set(); const onPropsCallback = jest.fn(); propListeners.add(onPropsCallback); - validateAndUpdateBrowserProps(window.xprops, propListeners, unrelatedEvent); + handleModalWrapperEvents(window.xprops, propListeners, unrelatedEvent); expect(onPropsCallback).toHaveBeenCalledTimes(0); }); From 80e8ac56e3fc515c60e2125482afb41b44e0a9bd Mon Sep 17 00:00:00 2001 From: Dan Haas Date: Tue, 1 Apr 2025 13:08:31 -0400 Subject: [PATCH 2/3] removes unnecessary window.focus call --- src/components/modal/v2/parts/Container.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/modal/v2/parts/Container.jsx b/src/components/modal/v2/parts/Container.jsx index c56c962f51..5611482ccf 100644 --- a/src/components/modal/v2/parts/Container.jsx +++ b/src/components/modal/v2/parts/Container.jsx @@ -45,8 +45,6 @@ const Container = ({ children }) => { useEffect(() => { if (transitionState === 'CLOSED') { contentWrapperRef.current.scrollTop = 0; - } else if (transitionState === 'OPEN') { - window.focus(); } }, [transitionState]); From a4a4cc45ed2ed2bde771c7e2657b7588f5b8b813 Mon Sep 17 00:00:00 2001 From: Dan Haas Date: Fri, 4 Apr 2025 13:42:35 -0400 Subject: [PATCH 3/3] simplifies by replacing constants with strings --- src/components/modal/v2/lib/postMessage.js | 2 +- src/components/modal/v2/lib/zoid-polyfill.js | 4 +--- .../spec/src/components/modal/v2/lib/zoid-polyfill.test.js | 7 ++----- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/components/modal/v2/lib/postMessage.js b/src/components/modal/v2/lib/postMessage.js index 6698fb7e8b..147874c46d 100644 --- a/src/components/modal/v2/lib/postMessage.js +++ b/src/components/modal/v2/lib/postMessage.js @@ -8,7 +8,7 @@ const POSTMESSENGER_EVENT_TYPES = { const POSTMESSENGER_ACK_PAYLOAD = { ok: 'true' }; - +// these constants should maintain parity with MESSAGE_MODAL_EVENT_NAMES in core-web-sdk export const POSTMESSENGER_EVENT_NAMES = { CALCULATE: 'paypal-messages-modal-calculate', CLOSE: 'paypal-messages-modal-close', diff --git a/src/components/modal/v2/lib/zoid-polyfill.js b/src/components/modal/v2/lib/zoid-polyfill.js index 1cdbb4ff35..e18784f913 100644 --- a/src/components/modal/v2/lib/zoid-polyfill.js +++ b/src/components/modal/v2/lib/zoid-polyfill.js @@ -6,8 +6,6 @@ import { sendEvent, createPostMessengerEvent, POSTMESSENGER_EVENT_NAMES } from ' const IOS_INTERFACE_NAME = 'paypalMessageModalCallbackHandler'; const ANDROID_INTERFACE_NAME = 'paypalMessageModalCallbackHandler'; -// these constants should maintain parity with MESSAGE_MODAL_EVENT_NAMES in core-web-sdk -export const WRAPPER_CLOSE_MESSAGE_NAME = 'MODAL_CLOSED'; function updateProps(newProps, propListeners) { Array.from(propListeners.values()).forEach(listener => { @@ -46,7 +44,7 @@ export function handleBrowserEvents(clientOrigin, propListeners, event) { if (eventName === 'PROPS_UPDATE') { handlePropsUpdateEvent(propListeners, event); } - if (eventName === WRAPPER_CLOSE_MESSAGE_NAME) { + if (eventName === 'MODAL_CLOSED') { logModalClose(event.data.eventPayload.linkName); } // send event ack with original event id so PostMessenger will stop reposting event diff --git a/tests/unit/spec/src/components/modal/v2/lib/zoid-polyfill.test.js b/tests/unit/spec/src/components/modal/v2/lib/zoid-polyfill.test.js index 7c899bc60f..68899e3e1c 100644 --- a/tests/unit/spec/src/components/modal/v2/lib/zoid-polyfill.test.js +++ b/tests/unit/spec/src/components/modal/v2/lib/zoid-polyfill.test.js @@ -1,7 +1,4 @@ -import zoidPolyfill, { - handleBrowserEvents, - WRAPPER_CLOSE_MESSAGE_NAME -} from 'src/components/modal/v2/lib/zoid-polyfill'; +import zoidPolyfill, { handleBrowserEvents } from 'src/components/modal/v2/lib/zoid-polyfill'; import { POSTMESSENGER_EVENT_NAMES } from 'src/components/modal/v2/lib/postMessage'; import { logger } from 'src/utils'; @@ -490,7 +487,7 @@ describe('zoidPollyfill', () => { const newPropsEvent = { origin: clientOrigin, data: { - eventName: WRAPPER_CLOSE_MESSAGE_NAME, + eventName: 'MODAL_CLOSED', eventPayload: { linkName: 'Custom Close Button' }