From ab7375a2f9bcc31e5bb8cd9e46d2960d3938dc2b Mon Sep 17 00:00:00 2001
From: jadutter <4691511+jadutter@users.noreply.github.com>
Date: Mon, 9 Oct 2023 15:40:04 -0400
Subject: [PATCH] separating debugging changes from hotfix/safari-modal-focus
---
src/components/message/Message.js | 17 +-
.../content/US-EZP/parts/ContentWrapper.jsx | 9 +-
.../modal/content/US/parts/Content.jsx | 4 +-
src/components/modal/lib/hooks/helpers.js | 1 +
src/components/modal/lib/providers/scroll.js | 23 +-
src/components/modal/lib/utils.js | 28 ++-
src/components/modal/parts/Overlay.jsx | 8 +-
src/components/modal/v2/lib/hooks/helpers.js | 1 +
.../modal/v2/lib/providers/scroll.js | 6 +-
.../modal/v2/lib/providers/transition.js | 6 +-
src/components/modal/v2/lib/utils.js | 27 ++-
src/components/modal/v2/parts/BodyContent.jsx | 32 ++-
src/components/modal/v2/parts/Header.jsx | 2 +
src/components/modal/v2/parts/Overlay.jsx | 8 +-
src/library/controllers/message/interface.js | 7 +-
src/library/controllers/message/setup.js | 12 +-
src/library/controllers/modal/interface.js | 12 +-
src/library/controllers/modal/setup.js | 5 +-
src/library/zoid/message/component.js | 38 +++-
.../zoid/message/containerTemplate.jsx | 30 ++-
src/library/zoid/modal/component.js | 34 ++-
src/library/zoid/modal/containerTemplate.jsx | 38 +++-
src/library/zoid/modal/prerenderTemplate.jsx | 97 ++++++--
src/library/zoid/treatments/component.js | 23 +-
src/utils/constants.js | 162 ++++++++++++++
src/utils/debug.js | 210 +++++++++++++++++-
src/utils/elements.js | 3 +-
src/utils/events.js | 11 +-
src/utils/experiments.js | 5 +-
src/utils/global.js | 5 +
src/utils/miscellaneous.js | 6 +-
src/utils/sdk.js | 17 ++
.../src/components/message/Message.test.js | 7 +-
33 files changed, 760 insertions(+), 134 deletions(-)
diff --git a/src/components/message/Message.js b/src/components/message/Message.js
index 3e6086d293..ae88a7f1dd 100644
--- a/src/components/message/Message.js
+++ b/src/components/message/Message.js
@@ -9,7 +9,8 @@ import {
parseObjFromEncoding,
getRequestDuration,
getTsCookieFromStorage,
- getOrCreateDeviceID
+ getOrCreateDeviceID,
+ MESSAGE_DOM_EVENT
} from '../../utils';
const Message = function ({ markup, meta, parentStyles, warnings }) {
@@ -56,9 +57,9 @@ const Message = function ({ markup, meta, parentStyles, warnings }) {
button.setAttribute('type', 'button');
- button.addEventListener('click', handleClick);
- button.addEventListener('mouseover', handleHover);
- button.addEventListener('focus', handleHover);
+ button.addEventListener(MESSAGE_DOM_EVENT.CLICK, handleClick);
+ button.addEventListener(MESSAGE_DOM_EVENT.MOUSEOVER, handleHover);
+ button.addEventListener(MESSAGE_DOM_EVENT.FOCUS, handleHover);
button.style.display = 'block';
button.style.background = 'transparent';
@@ -88,7 +89,8 @@ const Message = function ({ markup, meta, parentStyles, warnings }) {
warnings: serverData.warnings
});
- window.addEventListener('focus', () => {
+ window.addEventListener(MESSAGE_DOM_EVENT.FOCUS, () => {
+ // when the iframe for the message receives focus, put the focus on the button
button.focus();
});
@@ -178,7 +180,10 @@ const Message = function ({ markup, meta, parentStyles, warnings }) {
)
.slice(1);
- ppDebug('Updating message with new props...', { inZoid: true });
+ ppDebug('Updating message with new props...', {
+ debugObj: { index: window.xprops.index },
+ inZoid: true
+ });
request('GET', `${window.location.origin}/credit-presentment/smart/message?${query}`).then(
({ data: resData }) => {
diff --git a/src/components/modal/content/US-EZP/parts/ContentWrapper.jsx b/src/components/modal/content/US-EZP/parts/ContentWrapper.jsx
index 716bc316f6..867a90019c 100644
--- a/src/components/modal/content/US-EZP/parts/ContentWrapper.jsx
+++ b/src/components/modal/content/US-EZP/parts/ContentWrapper.jsx
@@ -6,6 +6,7 @@ import Header from '../../../parts/Header';
import Container from '../../../parts/Container';
import Button from '../../../parts/Button';
import { useApplyNow, useTransitionState } from '../../../lib';
+import { MODAL_DOM_EVENT } from '../../../../../utils';
const ContentWrapper = () => {
const contentWrapper = useRef();
@@ -23,12 +24,12 @@ const ContentWrapper = () => {
const handleApplyNowShow = () => !showApplyNow && setApplyNow(true);
const handleApplyNowHide = () => showApplyNow && setApplyNow(false);
- window.addEventListener('apply-now-visible', handleApplyNowShow);
- window.addEventListener('apply-now-hidden', handleApplyNowHide);
+ window.addEventListener(MODAL_DOM_EVENT.APPLY_NOW_VISIBLE, handleApplyNowShow);
+ window.addEventListener(MODAL_DOM_EVENT.APPLY_NOW_HIDDEN, handleApplyNowHide);
return () => {
- window.removeEventListener('apply-now-visible', handleApplyNowShow);
- window.removeEventListener('apply-now-hidden', handleApplyNowHide);
+ window.removeEventListener(MODAL_DOM_EVENT.APPLY_NOW_VISIBLE, handleApplyNowShow);
+ window.removeEventListener(MODAL_DOM_EVENT.APPLY_NOW_HIDDEN, handleApplyNowHide);
};
}, [showApplyNow]);
diff --git a/src/components/modal/content/US/parts/Content.jsx b/src/components/modal/content/US/parts/Content.jsx
index 60796bab20..3f687803ab 100644
--- a/src/components/modal/content/US/parts/Content.jsx
+++ b/src/components/modal/content/US/parts/Content.jsx
@@ -152,7 +152,9 @@ const Content = ({ headerRef, contentWrapper }) => {
Subject to credit approval.
-
+
+ {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions */}
+
diff --git a/src/library/zoid/treatments/component.js b/src/library/zoid/treatments/component.js
index 68e85344aa..305ded417e 100644
--- a/src/library/zoid/treatments/component.js
+++ b/src/library/zoid/treatments/component.js
@@ -4,7 +4,7 @@ import { node, dom } from '@krakenjs/jsx-pragmatic/src';
import { getCurrentScriptUID } from '@krakenjs/belter/src';
// Direct imports to avoid import cycle by importing from ../../../utils
-import { TAG } from '../../../utils/constants';
+import { GLOBAL_EVENT, TREATMENTS_EVENT, TAG } from '../../../utils/constants';
import {
getMeta,
getEnv,
@@ -16,7 +16,7 @@ import {
getFeatures
} from '../../../utils/sdk';
import { getGlobalUrl, createGlobalVariableGetter, globalEvent } from '../../../utils/global';
-import { ppDebug } from '../../../utils/debug';
+import { canDebug, DEBUG_CONDITIONS, getDebugLevel, ppDebug } from '../../../utils/debug';
export default createGlobalVariableGetter('__paypal_credit_treatments__', () =>
create({
@@ -25,7 +25,15 @@ export default createGlobalVariableGetter('__paypal_credit_treatments__', () =>
// eslint-disable-next-line security/detect-unsafe-regex
domain: /\.paypal\.com(:\d+)?$/,
- containerTemplate: ({ uid, prerenderFrame, frame, doc }) => {
+ containerTemplate: ({ uid, prerenderFrame, frame, doc, event }) => {
+ if (canDebug(DEBUG_CONDITIONS.EVENT_EMITTERS)) {
+ ppDebug(`EVENT_EMITTER.TREATMENTS`, { debugObj: event });
+ }
+ if (canDebug(DEBUG_CONDITIONS.ZOID_EVENTS) && typeof event?.on !== 'undefined') {
+ Object.entries(TREATMENTS_EVENT).forEach(([eventId, eventName]) =>
+ event?.on(eventName, debugObj => ppDebug(`EVENT.TREATMENTS.${eventId}`, { debugObj }))
+ );
+ }
const styles = `
#${uid} {
position: absolute!important;
@@ -72,6 +80,7 @@ export default createGlobalVariableGetter('__paypal_credit_treatments__', () =>
const TREATMENTS_MAX_AGE = 1000 * 60 * 15;
return ({ treatmentsHash, deviceID }) => {
+ ppDebug(`EVENT.TREATMENTS.onReady`, { debugObj: { treatmentsHash, deviceID } });
writeToLocalStorage({
experiments: {
treatmentsHash,
@@ -84,7 +93,7 @@ export default createGlobalVariableGetter('__paypal_credit_treatments__', () =>
id: deviceID
});
- globalEvent.trigger('treatments');
+ globalEvent.trigger(GLOBAL_EVENT.TREATMENTS);
};
}
},
@@ -134,6 +143,12 @@ export default createGlobalVariableGetter('__paypal_credit_treatments__', () =>
queryParam: true,
value: () => __MESSAGES__.__TARGET__,
debug: ppDebug(`Library Integration: ${__MESSAGES__.__TARGET__}`)
+ },
+ debug: {
+ type: 'number',
+ queryParam: 'pp_debug',
+ required: false,
+ value: getDebugLevel
}
}
})
diff --git a/src/utils/constants.js b/src/utils/constants.js
index a3c7dbcbcb..dbeebc0c7c 100644
--- a/src/utils/constants.js
+++ b/src/utils/constants.js
@@ -1,3 +1,160 @@
+/**
+ * We're manually defining these instead of importing to avoid
+ * an issue with webpack when it attempts to import `@krakenjs/zoid/src/constants`
+ * @see {@link https://github.com/krakenjs/zoid/blob/main/src/constants.js#L52-L64 zoid EVENT}
+ */
+const zoidEvent = {
+ RENDER: 'zoid-render',
+ RENDERED: 'zoid-rendered',
+ PRERENDER: 'zoid-prerender',
+ PRERENDERED: 'zoid-prerendered',
+ DISPLAY: 'zoid-display',
+ ERROR: 'zoid-error',
+ CLOSE: 'zoid-close',
+ DESTROY: 'zoid-destroy',
+ PROPS: 'zoid-props',
+ RESIZE: 'zoid-resize',
+ FOCUS: 'zoid-focus'
+};
+
+/**
+ * Events we are using on the EventEmitter for the message component.
+ * @const
+ * @enum
+ * @see {@link https://github.com/krakenjs/zoid/blob/main/src/constants.js#L52-L64 zoid EVENT}
+ */
+export const MESSAGE_EVENT = {
+ ...zoidEvent,
+ STYLE: 'styles'
+};
+
+/**
+ * Events we are using on the EventEmitter for the modal component.
+ * @const
+ * @enum
+ * @see {@link https://github.com/krakenjs/zoid/blob/main/src/constants.js#L52-L64 zoid EVENT}
+ */
+export const MODAL_EVENT = {
+ ...zoidEvent,
+ READY: 'ready',
+ PRERENDER_MODAL_DESTROY: 'prerender-modal-destroy',
+ MODAL_SHOW: 'modal-show',
+ MODAL_HIDE: 'modal-hide',
+ MODAL_FOCUS: 'modal-focus',
+ MODAL_BLUR: 'modal-blur'
+};
+
+/**
+ * Events we are using on the EventEmitter for the treatment component.
+ * @const
+ * @enum
+ * @see {@link https://github.com/krakenjs/zoid/blob/main/src/constants.js#L52-L64 zoid EVENT}
+ */
+export const TREATMENTS_EVENT = {
+ ...zoidEvent
+};
+
+/**
+ * Props on the message component that represent a function
+ * that will execute, that the merchant can hook into
+ * @const
+ * @enum
+ */
+export const MESSAGE_PROP_EVENT = {
+ CLICK: 'onClick',
+ APPLY: 'onApply',
+ HOVER: 'onHover',
+ MARKUP: 'onMarkup',
+ DESTROY: 'onDestroy',
+ CLOSE: 'onClose'
+};
+
+/**
+ * Props on the modal component that represent a function
+ * that will execute, that the merchant can hook into
+ * @const
+ * @enum
+ */
+export const MODAL_PROP_EVENT = {
+ CLICK: 'onClick',
+ CALCULATE: 'onCalculate',
+ SHOW: 'onShow',
+ CLOSE: 'onClose',
+ READY: 'onReady'
+};
+
+/**
+ * Events we are using on the `window` object *within*
+ * the iframe for the message component
+ * @const
+ * @enum
+ */
+export const MESSAGE_DOM_EVENT = {
+ DOM_CONTENT_LOADED: 'DOMContentLoaded',
+ CLICK: 'click',
+ MOUSEOVER: 'mouseover',
+ FOCUS: 'focus',
+ BLUR: 'blur'
+};
+
+/**
+ * Events we are using on the `window` object *within*
+ * the iframe for the modal component
+ * @const
+ * @enum
+ */
+export const MODAL_DOM_EVENT = {
+ DOM_CONTENT_LOADED: 'DOMContentLoaded',
+ CLICK: 'click',
+ MOUSEOVER: 'mouseover',
+ FOCUS: 'focus',
+ BLUR: 'blur',
+ KEYDOWN: 'keydown',
+ KEYUP: 'keyup',
+ SCROLL: 'scroll',
+ TOUCHMOVE: 'touchmove',
+ APPLY_NOW_VISIBLE: 'apply-now-visible',
+ APPLY_NOW_HIDDEN: 'apply-now-hidden'
+};
+
+/**
+ * Events we are using on the `window` object *within*
+ * the parent page (containing the message and modal components)
+ * @const
+ * @enum
+ */
+export const PARENT_DOM_EVENT = {
+ DOM_CONTENT_LOADED: 'DOMContentLoaded',
+ CLICK: 'click',
+ FOCUS: 'focus',
+ BLUR: 'blur',
+ KEYDOWN: 'keydown',
+ KEYUP: 'keyup',
+ LOAD: 'load',
+ MOUSEOVER: 'mouseover',
+ SCROLL: 'scroll',
+ TOUCHMOVE: 'touchmove'
+};
+
+/**
+ * Events we are using on the `globalEvent` EventEmitter
+ * @const
+ * @enum
+ */
+export const GLOBAL_EVENT = {
+ /** When we render our first element on the page (generally a message component) with a given set of properties. */
+ RENDER: 'render',
+ /** When the modal first renders */
+ MODAL_RENDER: 'modal-render',
+ /** When the modal renders */
+ TREATMENTS: 'treatments'
+};
+
+/**
+ * Buy Now Pay Later offers
+ * @const
+ * @enum
+ */
export const OFFER = {
PAY_LATER_LONG_TERM: 'PAY_LATER_LONG_TERM',
PAY_LATER_SHORT_TERM: 'PAY_LATER_SHORT_TERM',
@@ -6,6 +163,11 @@ export const OFFER = {
PAYPAL_CREDIT_NO_INTEREST: 'PAYPAL_CREDIT_NO_INTEREST'
};
+/**
+ * The tags we want our zoid components to use
+ * @const
+ * @enum
+ */
export const TAG = {
MESSAGE: 'paypal-message',
MODAL: 'paypal-credit-modal',
diff --git a/src/utils/debug.js b/src/utils/debug.js
index 4d0fef9504..71b4f24082 100644
--- a/src/utils/debug.js
+++ b/src/utils/debug.js
@@ -1,17 +1,205 @@
-import { isZoidComponent } from './sdk';
+import { isZoidComponent, whichComponent } from './sdk';
+import { GLOBAL_EVENT, MESSAGE_DOM_EVENT, MODAL_DOM_EVENT } from './constants';
+import { globalEvent } from './global';
+
/**
- * Messaging debug log. When the query param "pp_debug" is used on a page with a message and set to "true", a series of logs will appear
- * within the browser's developer tools console.
- * @param {string} message - The name of the parameter you are logging out. Also often times includes the parameter to be logged
- * within a template string.
- * @param {object} debugObj - Optional argument used in cases where the property you wish to log is an object. i.e.
- * an HTML element. ppDebug('Script:', { debugObj: script }).
- * @param {boolean} inZoid - Optional argument. Set inZoid to true if you wish to log inside of the iframe. i.e. ppDebug('Sample message', {inZoid: true}). Not using this
- * argument defaults inZoid to false.
+ * A set of identifiers to keep track of what will be shown
+ * with different levels of verbosity.
+ * @enum
+ */
+export const DEBUG_CONDITIONS = {
+ /** Show general information about PayPal Messaging that is useful to most interested parties */
+ GENERAL: 1,
+ /** the global events on the parent page */
+ GLOBAL_EVENTS: 10,
+ /** show the props assigned to a component */
+ PROPS: 15,
+ /** the events for components that merchants can hook into via props */
+ PROP_EVENTS: 20,
+ /** the zoid events for the components */
+ ZOID_EVENTS: 30,
+ /** DOM events for components, except `mouseover`, `scroll`, and `touchmove` events */
+ DOM_EVENTS: 40,
+ /** Provide the event emitters for components */
+ EVENT_EMITTERS: 50,
+ /**
+ * `mouseover`, `scroll`, and `touchmove` events for components; we separate these from the rest
+ * because we're expecting these to produce more spam than the other DOM events.
+ */
+ MOUSEOVER: 60
+};
+
+const debugCache = {};
+
+/**
+ * Determine the verbosity we should use for debugging.
+ * Higher numbers result in more verbose logging, with zero
+ * or less than zero corresponding to no debugging.
+ *
+ * We provide this function so that we can conditionally add
+ * logic that only needs to execute when debugging, such as
+ * adding event listeners that will call ppDebug
+ *
+ * @returns {number} debugLevel - an integer representing how verbose the debugging should be
+ */
+export function getDebugLevel() {
+ const {
+ // cache results to help with compute time
+ isZoid: cacheIsZoid,
+ href: cacheHref,
+ debugLevel: cacheDebugLevel
+ } = debugCache;
+ // if we don't know if we're in a zoid component, find out if we are
+ if (typeof cacheIsZoid === 'undefined') {
+ debugCache.isZoid = isZoidComponent();
+ }
+ // if we are in a zoid component, then get the debug level from the xprops
+ if (cacheIsZoid) {
+ return window?.xprops?.debug ?? 0;
+ }
+ // if we're on the parent page and the href hasn't changed since we last checked,
+ // then return the same debug value we initially got
+ if (window.location.href === cacheHref) {
+ return cacheDebugLevel;
+ }
+
+ // find `pp_debug` in the url set to `true` or an integer
+ // (up to three digits to prevent trying to match an excessive number of digits)
+ const debugPattern = /(?:\?|&)pp_debug=(?
true|\d{1,3})(?:&|$)/;
+ // determine what we were given
+ const debugArg = `${window?.location?.search}`.match(debugPattern)?.groups?.debug;
+ // turn it into an integer and cache the results
+ const level = Number(debugArg === 'true' ? 1 : debugArg);
+ debugCache.debugLevel = Number.isNaN(level) ? 0 : level;
+ debugCache.href = window.location.href;
+ return debugCache.debugLevel;
+}
+
+/**
+ * Check whether the debug verbosity level allows for a set of debugging
+ * information to be used.
+ * @param {number} condition - an entry from DEBUG_CONDITIONS that we wish to check
+ * @returns {boolean} result - whether we can provide debugging information for this condition
+ * @example
+ * if (canDebug(DEBUG_CONDITIONS.ZOID_EVENTS)){
+ * // only create event listeners if the debug verbosity is high enough
+ * Object.entries(MODAL_EVENT).forEach(([eventId, eventName]) =>
+ * event.on(eventName, debugObj => ppDebug(`EVENT.MODAL.${props.index}.${eventId}`, { debugObj }))
+ * );
+ * }
+ */
+export function canDebug(condition) {
+ return getDebugLevel() >= condition;
+}
+
+/**
+ * Create a messaging debug log.
+ * Adding the query parameter `pp_debug` with a value of `true` or an integer
+ * greater than zero to a page with a PayPal message will enable `ppDebug` logs
+ * to appear in the browser's developer tools console.
+ * @param {string} message - The name of the parameter you are logging out. Also often times
+ * includes the parameter to be logged within a template string.
+ * @param {object} [debugObj] - an object we wish to log to the console for further inspection
+ * @param {boolean} [inZoid=false] - Set inZoid to `true` if you wish to log inside of the iframe.
+ * @example
+ * // log within the parent page
+ * ppDebug('Sample message')
+ *
+ * // log within the parent page the script HTML element
+ * const script = document.querySelector('script')
+ * ppDebug('Sample message', { debugObj: script })
+ *
+ * // log within the iframe
+ * ppDebug('Sample message', { inZoid: true })
*/
export function ppDebug(message, { inZoid, debugObj } = {}) {
- if (/(\?|&)pp_debug=true(&|$)/.test(window.location.search) && !!inZoid === isZoidComponent()) {
+ if (canDebug(DEBUG_CONDITIONS.GENERAL) && !!inZoid === isZoidComponent()) {
// eslint-disable-next-line no-console
- console.warn('PayPal Messages Debug:', message, debugObj ?? '');
+ console.warn(
+ ...(typeof debugObj === 'undefined' || debugObj === ''
+ ? ['PayPal Messages Debug:', message]
+ : ['PayPal Messages Debug:', message, debugObj])
+ );
+ }
+}
+
+export function setupGlobalDebugListeners() {
+ if (canDebug(DEBUG_CONDITIONS.EVENT_EMITTERS)) {
+ ppDebug(`EVENT_EMITTER.GLOBAL`, { debugObj: globalEvent });
+ }
+ if (canDebug(DEBUG_CONDITIONS.GLOBAL_EVENTS)) {
+ Object.entries(GLOBAL_EVENT).forEach(([eventId, eventName]) =>
+ globalEvent.on(eventName, data => ppDebug(`EVENT.GLOBAL.${eventId}`, { debugObj: data }))
+ );
+ }
+}
+
+export function setupMessageDomDebugListeners() {
+ if (canDebug(DEBUG_CONDITIONS.DOM_EVENTS)) {
+ Object.entries(MESSAGE_DOM_EVENT)
+ .filter(([, eventName]) => {
+ // skip listening for ALL `keyup` and `keydown`; when we add a listener
+ // for a specific set of keys, then we'll also add a ppDebug for it.
+ if (MESSAGE_DOM_EVENT.KEYUP === eventName || MESSAGE_DOM_EVENT.KEYDOWN === eventName) {
+ return false;
+ }
+ // Also, we don't want mouseover to spam the console unless we're explicitly
+ // want it to be that verbose
+ if (MESSAGE_DOM_EVENT.MOUSEOVER === eventName && !canDebug(DEBUG_CONDITIONS.MOUSEOVER)) {
+ return false;
+ }
+ return true;
+ })
+ .forEach(([eventId, eventName]) => {
+ window.addEventListener(eventName, event => {
+ ppDebug(`EVENT.MESSAGE.${window?.xprops?.index}.${eventId}`, { inZoid: true, debugObj: event });
+ });
+ });
+ }
+}
+
+export function setupModalDomDebugListeners() {
+ if (canDebug(DEBUG_CONDITIONS.DOM_EVENTS)) {
+ Object.entries(MODAL_DOM_EVENT)
+ .filter(([, eventName]) => {
+ // skip listening for ALL `keyup` and `keydown`; when we add a listener
+ // for a specific set of keys, then we'll also add a ppDebug for it.
+ if (MODAL_DOM_EVENT.KEYUP === eventName || MODAL_DOM_EVENT.KEYDOWN === eventName) {
+ return false;
+ }
+ // Also, we don't want mouseover to spam the console unless we're explicitly
+ // want it to be that verbose
+ if (
+ [MODAL_DOM_EVENT.MOUSEOVER, MODAL_DOM_EVENT.SCROLL, MODAL_DOM_EVENT.TOUCHMOVE].includes(
+ eventName
+ ) &&
+ !canDebug(DEBUG_CONDITIONS.MOUSEOVER)
+ ) {
+ return false;
+ }
+ return true;
+ })
+ .forEach(([eventId, eventName]) => {
+ window.addEventListener(eventName, data =>
+ ppDebug(`EVENT.MODAL.${window?.xprops?.index}.${eventId}`, { inZoid: true, debugObj: data })
+ );
+ });
+ }
+}
+
+function setupDomDebugListeners() {
+ const component = whichComponent();
+ if (component === 'MESSAGE') {
+ setupMessageDomDebugListeners();
+ return;
+ }
+ if (component === 'MODAL') {
+ setupModalDomDebugListeners();
+ return;
+ }
+ if (component === 'TREATMENTS') {
+ return;
}
+ setupGlobalDebugListeners();
}
+setupDomDebugListeners();
diff --git a/src/utils/elements.js b/src/utils/elements.js
index 8f4b7638b8..84349bf219 100644
--- a/src/utils/elements.js
+++ b/src/utils/elements.js
@@ -8,6 +8,7 @@ import { ZalgoPromise } from '@krakenjs/zalgo-promise/src';
import { curry } from './functional';
import { objectMerge, flattenedToObject } from './objects';
import { ppDebug } from './debug';
+import { PARENT_DOM_EVENT } from './constants';
export const getWindowFromElement = node => node?.ownerDocument?.defaultView;
@@ -216,7 +217,7 @@ export const appendImage = curry((container, url, alt = 'PayPal Credit', srcset
export const waitForElementReady = element =>
new ZalgoPromise(resolve => {
if (element.tagName === 'IFRAME' && element.contentWindow.document.readyState !== 'complete') {
- element.addEventListener('load', resolve);
+ element.addEventListener(PARENT_DOM_EVENT.LOAD, resolve);
} else {
resolve();
}
diff --git a/src/utils/events.js b/src/utils/events.js
index a49e1a605a..f87b03cd9b 100644
--- a/src/utils/events.js
+++ b/src/utils/events.js
@@ -1,21 +1,22 @@
import { ZalgoPromise } from '@krakenjs/zalgo-promise/src';
import { globalEvent } from './global';
+import { PARENT_DOM_EVENT, GLOBAL_EVENT } from './constants';
-export const awaitTreatments = new ZalgoPromise(resolve => globalEvent.once('treatments', resolve));
+export const awaitTreatments = new ZalgoPromise(resolve => globalEvent.once(GLOBAL_EVENT.TREATMENTS, resolve));
-export const awaitFirstRender = new ZalgoPromise(resolve => globalEvent.once('render', resolve));
+export const awaitFirstRender = new ZalgoPromise(resolve => globalEvent.once(GLOBAL_EVENT.RENDER, resolve));
-export const awaitFirstModalRender = new ZalgoPromise(resolve => globalEvent.once('modal-render', resolve));
+export const awaitFirstModalRender = new ZalgoPromise(resolve => globalEvent.once(GLOBAL_EVENT.MODAL_RENDER, resolve));
export const awaitWindowLoad = new ZalgoPromise(resolve =>
typeof document !== 'undefined' && document.readyState !== 'complete'
- ? window.addEventListener('load', resolve)
+ ? window.addEventListener(PARENT_DOM_EVENT.LOAD, resolve)
: resolve()
);
export const awaitDOMContentLoaded = new ZalgoPromise(resolve =>
typeof document !== 'undefined' && document.readyState === 'loading'
- ? window.addEventListener('DOMContentLoaded', resolve)
+ ? window.addEventListener(PARENT_DOM_EVENT.DOM_CONTENT_LOADED, resolve)
: resolve()
);
diff --git a/src/utils/experiments.js b/src/utils/experiments.js
index ef90d5d49e..5aa480ef17 100644
--- a/src/utils/experiments.js
+++ b/src/utils/experiments.js
@@ -2,6 +2,7 @@ import { isLocalStorageEnabled } from '@krakenjs/belter/src';
import { getTreatmentsComponent } from '../library/zoid/treatments';
import { memoize } from './functional';
import { globalEvent } from './global';
+import { GLOBAL_EVENT, PARENT_DOM_EVENT } from './constants';
import { getStorage, isZoidComponent } from './sdk';
export const fetchTreatments = memoize(() => {
@@ -27,7 +28,7 @@ export function getLocalTreatments() {
// This ensures that unique event objects are not passed by the event listener into fetchTreatments,
// which avoids the benefit of memoization, because we only want the logic inside fetchTreatments
// to execute once, which only occurs when fetchTreatments is always called with ()
- window.addEventListener('DOMContentLoaded', () => fetchTreatments());
+ window.addEventListener(PARENT_DOM_EVENT.DOM_CONTENT_LOADED, () => fetchTreatments());
} else {
fetchTreatments();
}
@@ -43,7 +44,7 @@ export function ensureTreatments() {
// we can't get local treatments if local storage is not supported (this should be extremely rare)
!isLocalStorageEnabled()
) {
- globalEvent.trigger('treatments');
+ globalEvent.trigger(GLOBAL_EVENT.TREATMENTS);
return;
}
diff --git a/src/utils/global.js b/src/utils/global.js
index 6c71293296..2f152a0432 100644
--- a/src/utils/global.js
+++ b/src/utils/global.js
@@ -77,4 +77,9 @@ export const createGlobalVariableGetter = (variable, fn) => () => {
return getGlobalState()[variable];
};
+/**
+ * An event emitter for managing global events
+ * @const
+ * @name globalEvent
+ */
export const globalEvent = eventEmitter();
diff --git a/src/utils/miscellaneous.js b/src/utils/miscellaneous.js
index 80bf2a8e96..addadea324 100644
--- a/src/utils/miscellaneous.js
+++ b/src/utils/miscellaneous.js
@@ -9,7 +9,7 @@ import { ZalgoPromise } from '@krakenjs/zalgo-promise/src';
import { partial, memoize } from './functional';
import { getStorage } from './sdk';
-import { OFFER } from './constants';
+import { OFFER, PARENT_DOM_EVENT } from './constants';
/**
* Create a state object and pass back a reference and update function
@@ -128,13 +128,13 @@ export const dynamicImport = memoize(url => {
return new ZalgoPromise(resolve => {
const script = document.createElement('script');
script.src = url;
- script.addEventListener('load', () => {
+ script.addEventListener(PARENT_DOM_EVENT.LOAD, () => {
document.body.removeChild(script);
resolve();
});
if (document.readyState === 'loading') {
- window.addEventListener('DOMContentLoaded', () => document.body.appendChild(script));
+ window.addEventListener(PARENT_DOM_EVENT.DOM_CONTENT_LOADED, () => document.body.appendChild(script));
} else {
document.body.appendChild(script);
}
diff --git a/src/utils/sdk.js b/src/utils/sdk.js
index 4f61b49cfd..b31d0d3cdf 100644
--- a/src/utils/sdk.js
+++ b/src/utils/sdk.js
@@ -116,6 +116,10 @@ export function getScriptAttributes() {
}
}
+export function getPartnerAttributionId() {
+ return (getScriptAttributes() ?? {})[SDK_SETTINGS.PARTNER_ATTRIBUTION_ID] ?? null;
+}
+
export function getNamespace() {
if (__MESSAGES__.__TARGET__ === 'SDK') {
return getSDKNamespace();
@@ -134,6 +138,19 @@ export function isZoidComponent() {
return Object.values(TAG).some(tag => window.name.startsWith(`__zoid__${tag.replace(/-/g, '_')}`));
}
+/**
+ * Determine which kind of zoid iframe we are within
+ * @returns {string} result - null or one of the keys for TAG: MESSAGE, MODAL, TREATMENTS
+ */
+export function whichComponent() {
+ return Object.entries(TAG).reduce((result, [component, tag]) => {
+ if (result === null && window.name.startsWith(`__zoid__${tag.replace(/-/g, '_')}`)) {
+ return component;
+ }
+ return result;
+ }, null);
+}
+
export function getStorage() {
return getBelterStorage({ name: getNamespace() });
}
diff --git a/tests/unit/spec/src/components/message/Message.test.js b/tests/unit/spec/src/components/message/Message.test.js
index 2ba9217044..01925eaf65 100644
--- a/tests/unit/spec/src/components/message/Message.test.js
+++ b/tests/unit/spec/src/components/message/Message.test.js
@@ -28,7 +28,12 @@ jest.mock('src/utils', () => ({
})),
// eslint-disable-next-line no-console
ppDebug: jest.fn(() => console.log('PayPal Debug Message')),
- getRequestDuration: jest.fn(() => 123)
+ getRequestDuration: jest.fn(() => 123),
+ MESSAGE_DOM_EVENT: {
+ CLICK: 'click',
+ MOUSEOVER: 'mouseover',
+ FOCUS: 'focus'
+ }
}));
describe('Message', () => {