Skip to content

Commit 8980daa

Browse files
authored
Add PayPal App Switch Overlay (#2484)
1 parent d3dab87 commit 8980daa

File tree

11 files changed

+549
-2
lines changed

11 files changed

+549
-2
lines changed

Diff for: src/three-domain-secure/utils.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
getPayPalDomainRegex,
1313
} from "@paypal/sdk-client/src";
1414

15-
import { Overlay } from "../ui/overlay";
15+
import { Overlay } from "../ui/overlay/three-domain-secure";
1616

1717
import type { TDSProps } from "./types";
1818

Diff for: src/ui/buttons/props.js

+12
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,15 @@ export type ButtonExtensions = {|
501501
resume: () => void,
502502
|};
503503

504+
type ShowPayPalAppSwitchOverlay = {|
505+
focus: () => void,
506+
close: () => void,
507+
|};
508+
509+
type HidePayPalAppSwitchOverlay = {|
510+
close: () => void,
511+
|};
512+
504513
export type ButtonProps = {|
505514
// app switch properties
506515
appSwitchWhenAvailable: string,
@@ -515,6 +524,9 @@ export type ButtonProps = {|
515524
// Not passed to child iframe
516525
visibilityChangeHandler: () => void,
517526

527+
showPayPalAppSwitchOverlay: (args: ShowPayPalAppSwitchOverlay) => void,
528+
hidePayPalAppSwitchOverlay: (args: HidePayPalAppSwitchOverlay) => void,
529+
518530
fundingSource?: ?$Values<typeof FUNDING>,
519531
intent: $Values<typeof INTENT>,
520532
createOrder: CreateOrder,
File renamed without changes.

Diff for: src/ui/overlay/paypal-app-switch/overlay.jsx

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/* @flow */
2+
/** @jsx node */
3+
4+
import { animate, noop } from "@krakenjs/belter/src";
5+
import { node, type ElementNode } from "@krakenjs/jsx-pragmatic/src";
6+
import { LOGO_COLOR, PayPalRebrandLogo } from "@paypal/sdk-logos/src";
7+
import { type ZalgoPromise } from "@krakenjs/zalgo-promise/src";
8+
9+
import { getContainerStyle, getSandboxStyle } from "./style";
10+
11+
type OverlayProps = {|
12+
buttonSessionID: string,
13+
close: () => ZalgoPromise<void>,
14+
focus: () => ZalgoPromise<void>,
15+
|};
16+
17+
export function PayPalAppSwitchOverlay({
18+
close,
19+
focus,
20+
buttonSessionID,
21+
}: OverlayProps): ElementNode {
22+
const uid = `paypal-overlay-${buttonSessionID}`;
23+
const overlayIframeName = `__paypal_checkout_sandbox_${uid}__`;
24+
const nonce = "";
25+
const content = {
26+
windowMessage: "To finish, go back to the PayPal app.",
27+
continueMessage: "Return to PayPal",
28+
};
29+
30+
function closeCheckout(e) {
31+
e.preventDefault();
32+
e.stopPropagation();
33+
const overlay = document.getElementsByName(uid)?.[0];
34+
35+
animate(overlay, "hide-container", noop);
36+
close();
37+
38+
if (overlay) {
39+
// the delay is to allow the animation time to run
40+
setTimeout(() => {
41+
overlay.remove();
42+
}, 300);
43+
}
44+
}
45+
46+
function focusCheckout(e) {
47+
e.preventDefault();
48+
e.stopPropagation();
49+
50+
focus();
51+
}
52+
53+
const setupShowAnimation = () => (el) => {
54+
animate(el, "show-container", noop);
55+
};
56+
57+
return (
58+
<div
59+
id={uid}
60+
name={uid}
61+
onRender={setupShowAnimation()}
62+
class="paypal-checkout-sandbox"
63+
>
64+
<style nonce={nonce}>{getSandboxStyle({ uid })}</style>
65+
<iframe
66+
title="PayPal Checkout Overlay"
67+
name={overlayIframeName}
68+
scrolling="no"
69+
class="paypal-checkout-sandbox-iframe"
70+
>
71+
<html>
72+
<body>
73+
<div
74+
dir="auto"
75+
id={uid}
76+
onClick={focusCheckout}
77+
class="paypal-overlay-context-popup paypal-checkout-overlay"
78+
>
79+
<a
80+
href="#"
81+
class="paypal-checkout-close"
82+
onClick={closeCheckout}
83+
aria-label="close"
84+
role="button"
85+
/>
86+
<div class="paypal-checkout-modal">
87+
<div class="paypal-checkout-logo" dir="ltr">
88+
<PayPalRebrandLogo logoColor={LOGO_COLOR.WHITE} />
89+
</div>
90+
{content.windowMessage && (
91+
<div class="paypal-checkout-message">
92+
{content.windowMessage}
93+
</div>
94+
)}
95+
{content.continueMessage && (
96+
<div class="paypal-checkout-continue">
97+
{/* This handler should be guarded with e.stopPropagation.
98+
This will stop the event from bubbling up to the overlay click handler
99+
and causing unexpected behavior. */}
100+
<a onClick={focusCheckout} href="#">
101+
{content.continueMessage}
102+
</a>
103+
</div>
104+
)}
105+
</div>
106+
<style nonce={nonce}>{getContainerStyle({ uid })}</style>
107+
</div>
108+
</body>
109+
</html>
110+
</iframe>
111+
</div>
112+
);
113+
}

Diff for: src/ui/overlay/paypal-app-switch/style.jsx

+167
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/* @flow */
2+
3+
export function getSandboxStyle({ uid }: {| uid: string |}): string {
4+
return `
5+
#${uid}.paypal-checkout-sandbox {
6+
display: block;
7+
position: fixed;
8+
top: 0;
9+
left: 0;
10+
11+
width: 100%;
12+
height: 100%;
13+
width: 100vw;
14+
height: 100vh;
15+
max-width: 100%;
16+
max-height: 100%;
17+
min-width: 100%;
18+
min-height: 100%;
19+
20+
z-index: 2147483647;
21+
22+
animation-duration: 0.3s;
23+
animation-iteration-count: 1;
24+
animation-fill-mode: forwards !important;
25+
opacity: 0;
26+
}
27+
28+
#${uid}.paypal-checkout-sandbox .paypal-checkout-sandbox-iframe {
29+
display: block;
30+
position: absolute;
31+
top: 0;
32+
left: 0;
33+
width: 100%;
34+
height: 100%;
35+
}
36+
37+
@keyframes show-container {
38+
from {
39+
opacity: 0;
40+
}
41+
42+
to {
43+
opacity: 1;
44+
}
45+
}
46+
47+
@keyframes hide-container {
48+
from {
49+
opacity: 1;
50+
}
51+
52+
50% {
53+
opacity: 1;
54+
}
55+
56+
to {
57+
opacity: 0;
58+
}
59+
}
60+
`;
61+
}
62+
63+
export function getContainerStyle({ uid }: {| uid: string |}): string {
64+
return `
65+
#${uid} {
66+
position: absolute;
67+
z-index: 2147483647;
68+
top: 0;
69+
left: 0;
70+
width: 100%;
71+
height: 100%;
72+
73+
transform: translate3d(0, 0, 0);
74+
75+
background: radial-gradient(84.48% 50% at 50% 50%, #000 0%, rgba(0, 0, 0, 0.75) 100%);
76+
77+
color: #fff;
78+
}
79+
80+
#${uid} a {
81+
color: #fff;
82+
}
83+
84+
#${uid} .paypal-checkout-close:before,
85+
#${uid} .paypal-checkout-close:after {
86+
background-color: #fff;
87+
}
88+
89+
#${uid} a {
90+
text-decoration: none;
91+
}
92+
93+
#${uid} .paypal-checkout-modal {
94+
font-family: PayPalPlain-Regular, system-ui, -apple-system, Roboto, "Segoe UI", Helvetica-Neue, Helvetica, Arial, sans-serif;
95+
font-size: 14px;
96+
text-align: center;
97+
98+
box-sizing: border-box;
99+
width: 100%;
100+
max-width: 350px;
101+
top: 50%;
102+
left: 50%;
103+
position: absolute;
104+
transform: translateX(-50%) translateY(-50%);
105+
text-align: center;
106+
}
107+
108+
#${uid}.paypal-overlay-loading .paypal-checkout-message, #${uid}.paypal-overlay-loading .paypal-checkout-continue {
109+
display: none;
110+
}
111+
112+
#${uid} .paypal-checkout-modal .paypal-checkout-logo {
113+
cursor: pointer;
114+
margin-bottom: 8px;
115+
display: inline-block;
116+
}
117+
118+
#${uid} .paypal-checkout-modal .paypal-checkout-logo img {
119+
height: 44px;
120+
}
121+
122+
#${uid} .paypal-checkout-modal .paypal-checkout-message {
123+
font-size: 14px;
124+
line-height: 18px;
125+
padding: 8px 16px;
126+
}
127+
128+
#${uid} .paypal-checkout-modal .paypal-checkout-continue {
129+
font-size: 14px;
130+
line-height: 18px;
131+
padding: 8px 0;
132+
font-weight: bold;
133+
}
134+
135+
#${uid} .paypal-checkout-modal .paypal-checkout-continue a {
136+
border-bottom: 1px solid white;
137+
}
138+
139+
#${uid} .paypal-checkout-close {
140+
position: absolute;
141+
right: 16px;
142+
top: 16px;
143+
width: 24px;
144+
height: 24px;
145+
}
146+
147+
#${uid}.paypal-overlay-loading .paypal-checkout-close {
148+
display: none;
149+
}
150+
151+
#${uid} .paypal-checkout-close:before, .paypal-checkout-close:after {
152+
position: absolute;
153+
left: 11px;
154+
content: ' ';
155+
height: 24px;
156+
width: 2px;
157+
}
158+
159+
#${uid} .paypal-checkout-close:before {
160+
transform: rotate(45deg);
161+
}
162+
163+
#${uid} .paypal-checkout-close:after {
164+
transform: rotate(-45deg);
165+
}
166+
`;
167+
}

Diff for: src/ui/overlay/three-domain-secure/index.jsx

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/* @flow */
2+
3+
export * from "./overlay";
File renamed without changes.
File renamed without changes.

Diff for: src/zoid/buttons/component.jsx

+37-1
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ import {
8888
import { isFundingEligible } from "../../funding";
8989
import { getPixelComponent } from "../pixel";
9090
import { CLASS } from "../../constants";
91+
import { PayPalAppSwitchOverlay } from "../../ui/overlay/paypal-app-switch/overlay";
9192

9293
import { containerTemplate } from "./container";
9394
import { PrerenderedButtons } from "./prerender";
@@ -303,6 +304,41 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
303304
required: false,
304305
},
305306

307+
showPayPalAppSwitchOverlay: {
308+
type: "function",
309+
queryParam: false,
310+
value:
311+
({ props: { buttonSessionID } }) =>
312+
({ close, focus }) => {
313+
const overlay = (
314+
<PayPalAppSwitchOverlay
315+
buttonSessionID={buttonSessionID}
316+
close={close}
317+
focus={focus}
318+
/>
319+
).render(dom({ doc: document }));
320+
321+
document.body?.appendChild(overlay);
322+
},
323+
},
324+
325+
hidePayPalAppSwitchOverlay: {
326+
type: "function",
327+
queryParam: false,
328+
value:
329+
({ props: { buttonSessionID } }) =>
330+
({ close }) => {
331+
const overlay = document.getElementsByName(
332+
`paypal-overlay-${buttonSessionID}`
333+
)?.[0];
334+
335+
if (overlay) {
336+
close();
337+
overlay.remove();
338+
}
339+
},
340+
},
341+
306342
redirect: {
307343
type: "function",
308344
sendToChild: true,
@@ -358,7 +394,7 @@ export const getButtonsComponent: () => ButtonsComponent = memoize(() => {
358394
eventName: "paypal-visibilitychange",
359395
payload: {
360396
url: window.location.href,
361-
// eslint-disable-next-line compat/compat
397+
362398
visibilityState: document.visibilityState,
363399
},
364400
});

Diff for: test/integration/tests/button/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ import "./clone";
1717
import "./renderOrder";
1818
import "./nonce";
1919
import "./eligibility";
20+
import "./paypalAppSwitchOverlay";

0 commit comments

Comments
 (0)