@economist/web-apple-pay 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -0
- package/dist/index.js +24 -0
- package/dist/src/apple-pay-button.d.ts +17 -0
- package/dist/src/apple-pay-button.js +24 -0
- package/dist/src/apple-pay-button.stories.js +1300 -0
- package/dist/src/apple-pay-modal.stories.js +1142 -0
- package/dist/src/index.js +24 -0
- package/dist/src/stories/fixtures/offer.d.ts +2 -0
- package/dist/src/stories/fixtures/offer.js +28 -0
- package/dist/src/stories/utils/story-env.d.ts +11 -0
- package/dist/src/stories/utils/story-env.js +141 -0
- package/package.json +8 -1
package/dist/src/index.js
CHANGED
|
@@ -881,6 +881,7 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
881
881
|
#offer = null;
|
|
882
882
|
#country = "";
|
|
883
883
|
#rendering = false;
|
|
884
|
+
#readyPromise = null;
|
|
884
885
|
get offer() {
|
|
885
886
|
return this.#offer;
|
|
886
887
|
}
|
|
@@ -894,11 +895,34 @@ var ApplePayButton = class _ApplePayButton extends HTMLElement {
|
|
|
894
895
|
set country(value) {
|
|
895
896
|
this.#country = value;
|
|
896
897
|
}
|
|
898
|
+
/**
|
|
899
|
+
* Awaitable contract for the component's current render lifecycle.
|
|
900
|
+
*
|
|
901
|
+
* - If called **after** the element has been appended to the DOM, resolves
|
|
902
|
+
* once `connectedCallback` finishes — whether Apple Pay rendered
|
|
903
|
+
* successfully or was hidden (unavailable / suppressed by session).
|
|
904
|
+
* - If called **before** the element is appended (or if it has never been
|
|
905
|
+
* connected), resolves immediately to `undefined`. It does **not** wait
|
|
906
|
+
* for a future connection, so always `await whenReady()` after `appendChild`:
|
|
907
|
+
*
|
|
908
|
+
* ```ts
|
|
909
|
+
* container.appendChild(btn); // browser fires connectedCallback
|
|
910
|
+
* await btn.whenReady(); // waits for render to finish
|
|
911
|
+
* if (btn.style.display !== 'none') { /* available *\/ }
|
|
912
|
+
* ```
|
|
913
|
+
*/
|
|
914
|
+
whenReady() {
|
|
915
|
+
return this.#readyPromise ?? Promise.resolve();
|
|
916
|
+
}
|
|
897
917
|
async connectedCallback() {
|
|
898
918
|
if (this.#rendering || this.querySelector(".apple-pay-container")) {
|
|
899
919
|
return;
|
|
900
920
|
}
|
|
901
921
|
this.#rendering = true;
|
|
922
|
+
this.#readyPromise = this.#run();
|
|
923
|
+
await this.#readyPromise;
|
|
924
|
+
}
|
|
925
|
+
async #run() {
|
|
902
926
|
try {
|
|
903
927
|
const available = await isApplePayAvailable();
|
|
904
928
|
if (!available) {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// src/stories/fixtures/offer.ts
|
|
2
|
+
var STORYBOOK_SAMPLE_OFFER = {
|
|
3
|
+
sku_id: "TEG-APPLEPAY-STORY-001",
|
|
4
|
+
display_name: "Economist Digital",
|
|
5
|
+
price: {
|
|
6
|
+
purchase: 12,
|
|
7
|
+
pre_discount: 24,
|
|
8
|
+
currency_code: "USD",
|
|
9
|
+
discounted: true,
|
|
10
|
+
tax: { inclusive: false }
|
|
11
|
+
},
|
|
12
|
+
product: {
|
|
13
|
+
type: "subscription",
|
|
14
|
+
variant: "digital",
|
|
15
|
+
offer_type: "standard"
|
|
16
|
+
},
|
|
17
|
+
subscription_options: {
|
|
18
|
+
free_trial: false,
|
|
19
|
+
renewal_interval: { duration: 1, unit: "month" },
|
|
20
|
+
renewal_price: 12,
|
|
21
|
+
auto_renewal: true,
|
|
22
|
+
variable_term: false,
|
|
23
|
+
display_name: "Monthly"
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
export {
|
|
27
|
+
STORYBOOK_SAMPLE_OFFER
|
|
28
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SessionResponse } from '../../clients/payment-checkout/types';
|
|
2
|
+
export type ApplePayStoryEnvironmentOptions = {
|
|
3
|
+
applePaySupported: boolean;
|
|
4
|
+
sessionResponse?: SessionResponse;
|
|
5
|
+
sessionStatus?: number;
|
|
6
|
+
debugBypassEnabled?: boolean;
|
|
7
|
+
bypassApplePayChecks?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare const applyApplePayStoryEnvironment: ({ applePaySupported, sessionResponse, sessionStatus, debugBypassEnabled, bypassApplePayChecks, }: ApplePayStoryEnvironmentOptions) => void;
|
|
10
|
+
export declare const withApplePayStoryEnvironment: <TArgs>(options: ApplePayStoryEnvironmentOptions, renderFn: (args: TArgs) => HTMLElement) => (args: TArgs) => HTMLElement;
|
|
11
|
+
export declare const clickWhenElementReady: (root: ParentNode, selector: string, maxAttempts?: number) => void;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// src/clients/payment-checkout/config.ts
|
|
2
|
+
var runtimeConfig = null;
|
|
3
|
+
var configureApplePay = (config) => {
|
|
4
|
+
runtimeConfig = {
|
|
5
|
+
...config
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
var resetApplePayConfig = () => {
|
|
9
|
+
runtimeConfig = null;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/stories/utils/story-env.ts
|
|
13
|
+
var STORYBOOK_WALLET_BASE_URL = "https://storybook-wallet.economist.com";
|
|
14
|
+
var STORYBOOK_SAFARI_UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15";
|
|
15
|
+
var defaultSessionResponse = {
|
|
16
|
+
loggedIn: false,
|
|
17
|
+
userType: "new-eligible-user"
|
|
18
|
+
};
|
|
19
|
+
var resetStoryEnvironment = null;
|
|
20
|
+
var createFetchMock = (sessionResponse, sessionStatus = 200) => {
|
|
21
|
+
return async (input) => {
|
|
22
|
+
const requestUrl = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
23
|
+
if (requestUrl.endsWith("/v1/wallet/session")) {
|
|
24
|
+
if (sessionStatus >= 400) {
|
|
25
|
+
return new Response("Session initialization failed", {
|
|
26
|
+
status: sessionStatus,
|
|
27
|
+
statusText: "Session Init Error"
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
return new Response(JSON.stringify(sessionResponse), {
|
|
31
|
+
status: 200,
|
|
32
|
+
headers: { "content-type": "application/json" }
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return new Response("Not Found", {
|
|
36
|
+
status: 404,
|
|
37
|
+
statusText: "Not Found"
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
var applyApplePayStoryEnvironment = ({
|
|
42
|
+
applePaySupported,
|
|
43
|
+
sessionResponse = defaultSessionResponse,
|
|
44
|
+
sessionStatus = 200,
|
|
45
|
+
debugBypassEnabled = false,
|
|
46
|
+
bypassApplePayChecks = false
|
|
47
|
+
}) => {
|
|
48
|
+
if (resetStoryEnvironment) {
|
|
49
|
+
resetStoryEnvironment();
|
|
50
|
+
resetStoryEnvironment = null;
|
|
51
|
+
}
|
|
52
|
+
const originalFetch = globalThis.fetch;
|
|
53
|
+
const originalHref = window.location.href;
|
|
54
|
+
const originalUserAgent = navigator.userAgent;
|
|
55
|
+
const originalMaxTouchPoints = navigator.maxTouchPoints;
|
|
56
|
+
const originalApplePaySessionDescriptor = Object.getOwnPropertyDescriptor(
|
|
57
|
+
window,
|
|
58
|
+
"ApplePaySession"
|
|
59
|
+
);
|
|
60
|
+
const mockApplePaySession = {
|
|
61
|
+
canMakePayments: () => applePaySupported,
|
|
62
|
+
canMakePaymentsWithActiveCard: async () => applePaySupported
|
|
63
|
+
};
|
|
64
|
+
Object.defineProperty(window, "ApplePaySession", {
|
|
65
|
+
configurable: true,
|
|
66
|
+
writable: true,
|
|
67
|
+
value: mockApplePaySession
|
|
68
|
+
});
|
|
69
|
+
Object.defineProperty(navigator, "userAgent", {
|
|
70
|
+
configurable: true,
|
|
71
|
+
writable: true,
|
|
72
|
+
value: STORYBOOK_SAFARI_UA
|
|
73
|
+
});
|
|
74
|
+
Object.defineProperty(navigator, "maxTouchPoints", {
|
|
75
|
+
configurable: true,
|
|
76
|
+
writable: true,
|
|
77
|
+
value: 0
|
|
78
|
+
});
|
|
79
|
+
globalThis.fetch = createFetchMock(sessionResponse, sessionStatus);
|
|
80
|
+
const nextUrl = new URL(window.location.href);
|
|
81
|
+
if (bypassApplePayChecks) {
|
|
82
|
+
nextUrl.searchParams.set("BYPASS_APPLE_PAY_CHECKS", "1");
|
|
83
|
+
} else {
|
|
84
|
+
nextUrl.searchParams.delete("BYPASS_APPLE_PAY_CHECKS");
|
|
85
|
+
}
|
|
86
|
+
window.history.replaceState({}, "", nextUrl.toString());
|
|
87
|
+
configureApplePay({
|
|
88
|
+
merchantId: "merchant.com.economist.storybook",
|
|
89
|
+
walletApiBaseUrl: STORYBOOK_WALLET_BASE_URL,
|
|
90
|
+
debugBypassEnabled
|
|
91
|
+
});
|
|
92
|
+
resetStoryEnvironment = () => {
|
|
93
|
+
resetApplePayConfig();
|
|
94
|
+
globalThis.fetch = originalFetch;
|
|
95
|
+
window.history.replaceState({}, "", originalHref);
|
|
96
|
+
Object.defineProperty(navigator, "userAgent", {
|
|
97
|
+
configurable: true,
|
|
98
|
+
writable: true,
|
|
99
|
+
value: originalUserAgent
|
|
100
|
+
});
|
|
101
|
+
Object.defineProperty(navigator, "maxTouchPoints", {
|
|
102
|
+
configurable: true,
|
|
103
|
+
writable: true,
|
|
104
|
+
value: originalMaxTouchPoints
|
|
105
|
+
});
|
|
106
|
+
if (originalApplePaySessionDescriptor) {
|
|
107
|
+
Object.defineProperty(
|
|
108
|
+
window,
|
|
109
|
+
"ApplePaySession",
|
|
110
|
+
originalApplePaySessionDescriptor
|
|
111
|
+
);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
delete window.ApplePaySession;
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
var withApplePayStoryEnvironment = (options, renderFn) => (args) => {
|
|
118
|
+
applyApplePayStoryEnvironment(options);
|
|
119
|
+
return renderFn(args);
|
|
120
|
+
};
|
|
121
|
+
var clickWhenElementReady = (root, selector, maxAttempts = 5) => {
|
|
122
|
+
let attempts = 0;
|
|
123
|
+
const tryClick = () => {
|
|
124
|
+
const element = root.querySelector(selector);
|
|
125
|
+
if (element) {
|
|
126
|
+
element.click();
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (attempts >= maxAttempts) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
attempts += 1;
|
|
133
|
+
requestAnimationFrame(tryClick);
|
|
134
|
+
};
|
|
135
|
+
requestAnimationFrame(tryClick);
|
|
136
|
+
};
|
|
137
|
+
export {
|
|
138
|
+
applyApplePayStoryEnvironment,
|
|
139
|
+
clickWhenElementReady,
|
|
140
|
+
withApplePayStoryEnvironment
|
|
141
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@economist/web-apple-pay",
|
|
3
3
|
"description": "Web component package for Apple Pay checkout UI",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.4.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
"scripts": {
|
|
23
23
|
"test": "jest",
|
|
24
24
|
"test:coverage": "npm run test -- --coverage",
|
|
25
|
+
"storybook": "storybook dev -p 6006",
|
|
26
|
+
"build-storybook": "storybook build",
|
|
25
27
|
"lint": "eslint .",
|
|
26
28
|
"lint:fix": "npm run lint -- --fix",
|
|
27
29
|
"tsc": "tsc --project tsconfig.json",
|
|
@@ -32,6 +34,10 @@
|
|
|
32
34
|
},
|
|
33
35
|
"devDependencies": {
|
|
34
36
|
"@eslint/js": "^9.25.1",
|
|
37
|
+
"@storybook/addon-essentials": "^8.6.18",
|
|
38
|
+
"@storybook/addon-interactions": "^8.6.18",
|
|
39
|
+
"@storybook/test": "^8.6.18",
|
|
40
|
+
"@storybook/web-components-vite": "^8.6.18",
|
|
35
41
|
"@types/jest": "29.5.3",
|
|
36
42
|
"esbuild": "^0.25.3",
|
|
37
43
|
"eslint": "^9.25.1",
|
|
@@ -39,6 +45,7 @@
|
|
|
39
45
|
"jest": "^29.7.0",
|
|
40
46
|
"jest-environment-jsdom": "^29.7.0",
|
|
41
47
|
"prettier": "3.0.1",
|
|
48
|
+
"storybook": "^8.6.18",
|
|
42
49
|
"ts-jest": "29.2.5",
|
|
43
50
|
"ts-node": "10.9.1",
|
|
44
51
|
"typescript": "^5.8.2",
|