diff --git a/.eslintrc b/.eslintrc index 9ba35ca26f..c559b35178 100644 --- a/.eslintrc +++ b/.eslintrc @@ -10,7 +10,7 @@ "__MESSAGING_GLOBALS__": true, "__ENV__": true }, - "plugins": ["prettier"], + "plugins": ["prettier","eslint-comments"], "rules": { "arrow-body-style": "off", "unicorn/prefer-spread": "off", diff --git a/src/components/modal/lib/utils.js b/src/components/modal/lib/utils.js index 2e0ae19780..29dcac8319 100644 --- a/src/components/modal/lib/utils.js +++ b/src/components/modal/lib/utils.js @@ -55,18 +55,14 @@ 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]; + e.preventDefault(); + tabArray[tabArray.length - 1].focus(); } else if (document.activeElement === tabArray[tabArray.length - 1]) { - // eslint-disable-next-line prefer-destructuring - nextElement = tabArray[0]; - } - - if (typeof nextElement !== 'undefined') { e.preventDefault(); - nextElement.focus(); + tabArray[0].focus(); } } } diff --git a/src/library/zoid/modal/containerTemplate.jsx b/src/library/zoid/modal/containerTemplate.jsx index c66cce4bd2..e4785bde4f 100644 --- a/src/library/zoid/modal/containerTemplate.jsx +++ b/src/library/zoid/modal/containerTemplate.jsx @@ -42,6 +42,8 @@ export default ({ uid, frame, prerenderFrame, doc, event, state, props: { cspNon requestAnimationFrame(() => { if (renderedModal) { frame.focus(); + } else if (window.document.activeElement !== prerenderFrame) { + prerenderFrame.focus(); } }); }); @@ -66,7 +68,7 @@ export default ({ uid, frame, prerenderFrame, doc, event, state, props: { cspNon }; const handleEscape = evt => { - if (state.open && (evt.key === 'Escape' || evt.key === 'Esc' || evt.charCode === 27)) { + if (state.open && (`${evt?.key}`.toLowerCase().startsWith('esc') || evt.charCode === 27)) { handleHide(); } }; @@ -78,7 +80,7 @@ export default ({ uid, frame, prerenderFrame, doc, event, state, props: { cspNon .then(() => ZalgoPromise.delay(TRANSITION_DELAY)) .then(() => destroyElement(prerenderFrame)) .then(() => { - if (state.open) { + if (state.open && document.activeElement !== frame) { frame.focus(); } }); diff --git a/src/library/zoid/modal/prerenderTemplate.jsx b/src/library/zoid/modal/prerenderTemplate.jsx index 2c56eab401..a8c3000e46 100644 --- a/src/library/zoid/modal/prerenderTemplate.jsx +++ b/src/library/zoid/modal/prerenderTemplate.jsx @@ -3,7 +3,7 @@ import { node, dom } from '@krakenjs/jsx-pragmatic/src'; import { Spinner } from '@paypal/common-components'; import { ZalgoPromise } from '@krakenjs/zalgo-promise/src'; -export default ({ doc, props, event }) => { +export default ({ doc, props, event, state }) => { const ERROR_DELAY = 15000; const styles = ` @font-face { @@ -97,15 +97,15 @@ export default ({ doc, props, event }) => { height: 48px; } - .error{ + #modal-status{ color: white; - width: 200px; - height: 100px; position: absolute; top: 67%; - left: 50%; + left: calc( 50% - 10px ); margin-left: -60px; display: none; + padding: 10px; + } @media (max-width: 639px), (max-height: 539px){ @@ -119,27 +119,54 @@ export default ({ doc, props, event }) => { } `; + let renderedModal = false; + let closeBtn; - const closeModal = () => event.trigger('modal-hide'); const checkForErrors = element => { ZalgoPromise.delay(ERROR_DELAY).then(() => { - const errorElement = element.querySelector('#errMsg'); - // check to see if modal content class exists - if (errorElement) { - // looks like there is an error if modal content class does not exist. + const modalStatus = element.querySelector('#modal-status'); + // if we have a place to put our status message, + // and we have not heard the 'zoid-rendered' event for the modal yet + if (modalStatus && !renderedModal) { // assign variable to state and access in UI - errorElement.style.display = 'block'; - errorElement.textContent = 'Error loading Modal'; + modalStatus.style.display = 'block'; + modalStatus.textContent = 'Error loading Modal'; // TODO: should we report this failure to our log endpoint? } }); }; - const focusCloseButton = element => { - window.requestAnimationFrame(() => { - // TODO: determine how to get this to re-focus if the prerender is dismissed and re-opened - element.focus(); + + const focusCloseButton = () => { + if (closeBtn) { + window.requestAnimationFrame(() => { + // TODO: determine how to get this to re-focus if the prerender is dismissed and re-opened + closeBtn.focus(); + }); + } + }; + + const handleClose = () => { + event.trigger('modal-hide'); + }; + + const handleEscape = evt => { + if (!renderedModal && state.open && (`${evt?.key}`.toLowerCase().startsWith('esc') || evt?.charCode === 27)) { + handleClose(); + } + }; + + const handleRender = element => { + closeBtn = element.querySelector('#prerender-close-btn'); + focusCloseButton(); + ZalgoPromise.delay(ERROR_DELAY).then(() => { + return checkForErrors(element); }); }; + + event.on('zoid-rendered', () => { + renderedModal = true; + }); + return (
@@ -147,19 +174,25 @@ export default ({ doc, props, event }) => { - +