Skip to content

Commit

Permalink
separating debugging changes from hotfix/safari-modal-focus
Browse files Browse the repository at this point in the history
  • Loading branch information
jadutter committed Oct 9, 2023
1 parent 9e2baa1 commit ab7375a
Show file tree
Hide file tree
Showing 33 changed files with 760 additions and 134 deletions.
17 changes: 11 additions & 6 deletions src/components/message/Message.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
parseObjFromEncoding,
getRequestDuration,
getTsCookieFromStorage,
getOrCreateDeviceID
getOrCreateDeviceID,
MESSAGE_DOM_EVENT
} from '../../utils';

const Message = function ({ markup, meta, parentStyles, warnings }) {
Expand Down Expand Up @@ -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';
Expand Down Expand Up @@ -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();
});

Expand Down Expand Up @@ -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 }) => {
Expand Down
9 changes: 5 additions & 4 deletions src/components/modal/content/US-EZP/parts/ContentWrapper.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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]);

Expand Down
4 changes: 3 additions & 1 deletion src/components/modal/content/US/parts/Content.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,9 @@ const Content = ({ headerRef, contentWrapper }) => {
<div>Subject to credit approval.</div>
<hr className="divider" />
</div>
<main className="main">{tabsContent}</main>
<main className="main" aria-label={`More info on the Pay Later offer${tabs.length > 1 ? 's' : ''}.`}>
{tabsContent}
</main>
</div>
);
};
Expand Down
1 change: 1 addition & 0 deletions src/components/modal/lib/hooks/helpers.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useEffect, useLayoutEffect, useRef } from 'preact/hooks';

// useAutoFocus unused?
export function useAutoFocus() {
const ref = useRef();

Expand Down
23 changes: 17 additions & 6 deletions src/components/modal/lib/providers/scroll.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
/** @jsx h */
import { h, createContext } from 'preact';
import { useEffect, useState, useContext, useCallback } from 'preact/hooks';
import { getEventListenerPassiveOptionIfSupported } from '../../../../utils';
import {
getEventListenerPassiveOptionIfSupported,
canDebug,
ppDebug,
DEBUG_CONDITIONS,
MODAL_DOM_EVENT
} from '../../../../utils';

const ScrollContext = createContext({
addScrollCallback: () => {},
Expand Down Expand Up @@ -34,16 +40,21 @@ export const ScrollProvider = ({ children, containerRef }) => {
};

useEffect(() => {
const handleScroll = event => callbacks.forEach(callback => callback(event));
const handleScroll = event => {
if (canDebug(DEBUG_CONDITIONS.MOUSEOVER)) {
ppDebug(`EVENT.MODAL.${window?.xprops?.index}.SCROLL`, { debugObj: event });
}
callbacks.forEach(callback => callback(event));
};

const passiveOption = getEventListenerPassiveOptionIfSupported();

containerRef.current.addEventListener('scroll', handleScroll, passiveOption);
containerRef.current.addEventListener('touchmove', handleScroll, passiveOption);
containerRef.current.addEventListener(MODAL_DOM_EVENT.SCROLL, handleScroll, passiveOption);
containerRef.current.addEventListener(MODAL_DOM_EVENT.TOUCHMOVE, handleScroll, passiveOption);

return () => {
containerRef.current.removeEventListener('scroll', handleScroll, passiveOption);
containerRef.current.removeEventListener('touchmove', handleScroll, passiveOption);
containerRef.current.removeEventListener(MODAL_DOM_EVENT.SCROLL, handleScroll, passiveOption);
containerRef.current.removeEventListener(MODAL_DOM_EVENT.TOUCHMOVE, handleScroll, passiveOption);
};
}, [callbacks]);

Expand Down
28 changes: 22 additions & 6 deletions src/components/modal/lib/utils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import arrayFrom from 'core-js-pure/stable/array/from';
import objectEntries from 'core-js-pure/stable/object/entries';
import { request, memoize, ppDebug } from '../../../utils';
import { request, memoize, canDebug, DEBUG_CONDITIONS, ppDebug, MODAL_DOM_EVENT } from '../../../utils';

export const getContent = memoize(
({
Expand Down Expand Up @@ -55,15 +55,31 @@ export function setupTabTrap() {
const tabArray = arrayFrom(document.querySelectorAll(focusableElementsString)).filter(
node => window.getComputedStyle(node).visibility === 'visible'
);
let nextElement;
// SHIFT + TAB
if (e.shiftKey && document.activeElement === tabArray[0]) {
nextElement = tabArray[tabArray.length - 1];
} else if (document.activeElement === tabArray[tabArray.length - 1]) {
// eslint-disable-next-line prefer-destructuring
nextElement = tabArray[0];
}

if (typeof nextElement !== 'undefined') {
e.preventDefault();
tabArray[tabArray.length - 1].focus();
} else if (!e.shiftKey && document.activeElement === tabArray[tabArray.length - 1]) {
e.preventDefault();
tabArray[0].focus();
nextElement.focus();
}

if (canDebug(DEBUG_CONDITIONS.DOM_EVENTS)) {
// give the document 10ms to update before printing a debug log
// showing the currently selected element
setTimeout(() => {
ppDebug(`EVENT.MODAL.${window?.xprops?.index}.KEYDOWN.${e.shiftKey ? 'SHIFT_TAB' : 'TAB'}`, {
inZoid: true,
debugObj: nextElement ?? document.activeElement
});
}, 10);
}
}
}
window.addEventListener('keydown', trapTabKey);
window.addEventListener(MODAL_DOM_EVENT.KEYDOWN, trapTabKey);
}
8 changes: 6 additions & 2 deletions src/components/modal/parts/Overlay.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,24 @@ import { h, Fragment } from 'preact';
import { useEffect } from 'preact/hooks';

import { useTransitionState } from '../lib';
import { canDebug, DEBUG_CONDITIONS, MODAL_DOM_EVENT, ppDebug } from '../../../utils';

const Overlay = ({ contentMaxWidth, contentMaxHeight }) => {
const [, handleClose] = useTransitionState();

useEffect(() => {
const handleEscapeKeyPress = evt => {
if (evt.key === 'Escape' || evt.key === 'Esc' || evt.charCode === 27) {
if (canDebug(DEBUG_CONDITIONS.DOM_EVENTS)) {
ppDebug(`EVENT.MODAL.${window?.xprops?.index}.KEYUP.ESCAPE`, { inZoid: true, debugObj: evt });
}
handleClose('Escape Key');
}
};

window.addEventListener('keyup', handleEscapeKeyPress);
window.addEventListener(MODAL_DOM_EVENT.KEYUP, handleEscapeKeyPress);

return () => window.removeEventListener('keyup', handleEscapeKeyPress);
return () => window.removeEventListener(MODAL_DOM_EVENT.KEYUP, handleEscapeKeyPress);
});

// Overlay must be split because the content wrapper fills
Expand Down
1 change: 1 addition & 0 deletions src/components/modal/v2/lib/hooks/helpers.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useEffect, useLayoutEffect, useRef } from 'preact/hooks';

// useAutoFocus unused?
export function useAutoFocus() {
const ref = useRef();

Expand Down
6 changes: 3 additions & 3 deletions src/components/modal/v2/lib/providers/scroll.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/** @jsx h */
import { h, createContext } from 'preact';
import { useEffect, useRef, useContext, useCallback } from 'preact/hooks';
import { getEventListenerPassiveOptionIfSupported } from '../../../../../utils';
import { getEventListenerPassiveOptionIfSupported, MODAL_DOM_EVENT } from '../../../../../utils';

const ScrollContext = createContext({
addScrollCallback: () => {},
Expand Down Expand Up @@ -34,10 +34,10 @@ export const ScrollProvider = ({ children, containerRef }) => {
const handleScroll = event => callbacksRef.current.forEach(callback => callback(event));
const passiveOption = getEventListenerPassiveOptionIfSupported();

containerRef.current.addEventListener('scroll', handleScroll, passiveOption);
containerRef.current.addEventListener(MODAL_DOM_EVENT.SCROLL, handleScroll, passiveOption);

return () => {
containerRef.current.removeEventListener('scroll', handleScroll, passiveOption);
containerRef.current.removeEventListener(MODAL_DOM_EVENT.SCROLL, handleScroll, passiveOption);
};
}, [containerRef.current]);

Expand Down
6 changes: 4 additions & 2 deletions src/components/modal/v2/lib/providers/transition.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ export const TransitionStateProvider = ({ children }) => {
* Particularly useful for those using screen readers and other accessibility functions.
*/
const focusCloseBtnOnModalOpen = () => {
const btn = document.querySelector('.close');
btn?.focus();
// give the document time to update before trying to focus the close button
setTimeout(() => {
document.querySelector('.close')?.focus();
}, 10);
};

useEffect(() => {
Expand Down
27 changes: 21 additions & 6 deletions src/components/modal/v2/lib/utils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import objectEntries from 'core-js-pure/stable/object/entries';
import arrayFrom from 'core-js-pure/stable/array/from';
import { isIosWebview, isAndroidWebview } from '@krakenjs/belter/src';
import { request, memoize, ppDebug } from '../../../../utils';
import { request, memoize, DEBUG_CONDITIONS, canDebug, ppDebug, MODAL_DOM_EVENT } from '../../../../utils';

export const getContent = memoize(
({
Expand Down Expand Up @@ -54,7 +54,7 @@ export const getContent = memoize(
)
.slice(1);

ppDebug('Updating modal with new props...', { inZoid: true });
ppDebug('Updating modal with new props...', { inZoid: true, debugObj: { index: window?.xprops?.index } });

return request('GET', `${window.location.origin}/credit-presentment/modalContent?${query}`).then(
({ data }) => data
Expand All @@ -80,17 +80,32 @@ export function setupTabTrap() {
const tabArray = arrayFrom(document.querySelectorAll(focusableElementsString)).filter(
node => window.getComputedStyle(node).visibility === 'visible'
);
let nextElement;
// SHIFT + TAB
if (e.shiftKey && document.activeElement === tabArray[0]) {
e.preventDefault();
tabArray[tabArray.length - 1].focus();
nextElement = tabArray[tabArray.length - 1];
} else if (document.activeElement === tabArray[tabArray.length - 1]) {
// eslint-disable-next-line prefer-destructuring
nextElement = tabArray[0];
}
if (typeof nextElement !== 'undefined') {
e.preventDefault();
tabArray[0].focus();
nextElement.focus();
}

if (canDebug(DEBUG_CONDITIONS.DOM_EVENTS)) {
// give the document 10ms to update before printing a debug log
// showing the currently selected element
setTimeout(() => {
ppDebug(`EVENT.MODAL.${window?.xprops?.index}.KEYDOWN.${e.shiftKey ? 'SHIFT_TAB' : 'TAB'}`, {
inZoid: true,
debugObj: nextElement ?? document.activeElement
});
}, 10);
}
}
}
window.addEventListener('keydown', trapTabKey);
window.addEventListener(MODAL_DOM_EVENT.KEYDOWN, trapTabKey);
}

export function formatDateByCountry(country) {
Expand Down
32 changes: 22 additions & 10 deletions src/components/modal/v2/parts/BodyContent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ import {
import Header from './Header';
import { LongTerm, ShortTerm, NoInterest, ProductList, PayIn1 } from './views';

const VIEW_IDS = {
// TODO: add an error view in case we receive an invalid view?
PAYPAL_CREDIT_NO_INTEREST: 'PAYPAL_CREDIT_NO_INTEREST',
PAY_LATER_LONG_TERM: 'PAY_LATER_LONG_TERM',
PAY_LATER_PAY_IN_1: 'PAY_LATER_PAY_IN_1',
PAY_LATER_SHORT_TERM: 'PAY_LATER_SHORT_TERM',
PRODUCT_LIST: 'PRODUCT_LIST'
};

const BodyContent = () => {
const { views } = useServerData();
const { offer } = useXProps();
Expand All @@ -29,12 +38,12 @@ const BodyContent = () => {

let defaultViewName;

const productViews = views.filter(view => view?.meta?.product !== 'PRODUCT_LIST');
const hasProductList = views.find(view => view?.meta?.product === 'PRODUCT_LIST');
const productViews = views.filter(view => view?.meta?.product !== VIEW_IDS.PRODUCT_LIST);
const hasProductList = views.find(view => view?.meta?.product === VIEW_IDS.PRODUCT_LIST);
if (productViews?.length === 1) {
defaultViewName = productViews[0]?.meta?.product;
} else if (productViews?.length > 1 && hasProductList) {
defaultViewName = 'PRODUCT_LIST';
defaultViewName = VIEW_IDS.PRODUCT_LIST;
} else if (productViews?.length > 1 && !hasProductList) {
defaultViewName = productViews[0]?.meta?.product;
}
Expand All @@ -49,7 +58,7 @@ const BodyContent = () => {
const { headline, subheadline, qualifyingSubheadline = '', closeButtonLabel } = content;
const isQualifying = productMeta?.qualifying;

const openProductList = () => setViewName('PRODUCT_LIST');
const openProductList = () => setViewName(VIEW_IDS.PRODUCT_LIST);

useDidUpdateEffect(() => {
scrollTo(0); // Reset scroll position to top when view changes
Expand All @@ -71,13 +80,13 @@ const BodyContent = () => {

// Add views to viewComponents object where the keys are the product name and the values are the view component
const viewComponents = {
PAYPAL_CREDIT_NO_INTEREST: <NoInterest content={content} openProductList={openProductList} />,
PAY_LATER_LONG_TERM: <LongTerm content={content} openProductList={openProductList} />,
PAY_LATER_PAY_IN_1: <PayIn1 content={content} openProductList={openProductList} />,
PAY_LATER_SHORT_TERM: (
[VIEW_IDS.PAYPAL_CREDIT_NO_INTEREST]: <NoInterest content={content} openProductList={openProductList} />,
[VIEW_IDS.PAY_LATER_LONG_TERM]: <LongTerm content={content} openProductList={openProductList} />,
[VIEW_IDS.PAY_LATER_PAY_IN_1]: <PayIn1 content={content} openProductList={openProductList} />,
[VIEW_IDS.PAY_LATER_SHORT_TERM]: (
<ShortTerm content={content} productMeta={productMeta} openProductList={openProductList} />
),
PRODUCT_LIST: <ProductList content={content} setViewName={setViewName} />
[VIEW_IDS.PRODUCT_LIST]: <ProductList content={content} setViewName={setViewName} />
};

// IMPORTANT: These elements cannot be nested inside of other elements.
Expand All @@ -95,7 +104,10 @@ const BodyContent = () => {
viewName={viewName}
/>
<div className="content__container">
<main className="main">
<main
className="main"
aria-label={`More info on the Pay Later offer${viewName === VIEW_IDS.PRODUCT_LIST ? 's' : ''}.`}
>
<div className="content__body">{viewComponents[viewName]}</div>
</main>
</div>
Expand Down
2 changes: 2 additions & 0 deletions src/components/modal/v2/parts/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ const Header = ({
aria-label={closeButtonLabel}
type="button"
id="close-btn"
autofocus
aria-keyshortcuts="escape"
onClick={() => handleClose('Close Button')}
>
<Icon name="close" />
Expand Down
Loading

0 comments on commit ab7375a

Please sign in to comment.