@paypal/checkout-components 5.0.316 → 5.0.317

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paypal/checkout-components",
3
- "version": "5.0.316",
3
+ "version": "5.0.317",
4
4
  "description": "PayPal Checkout components, for integrating checkout products.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,7 +1,4 @@
1
1
  /* @flow */
2
-
3
- import { getLogger } from "@paypal/sdk-client/src";
4
-
5
2
  import { getButtonsComponent } from "../zoid/buttons";
6
3
 
7
4
  import {
@@ -10,29 +7,36 @@ import {
10
7
  getHostedButtonDetails,
11
8
  renderForm,
12
9
  getMerchantID,
13
- shouldRenderSDKButtons,
14
10
  getFlexDirection,
15
- appendButtonContainer,
16
- getButtonColor,
11
+ applyContainerStyles,
12
+ renderStandaloneButton,
13
+ renderDefaultButton,
14
+ getDefaultButtonOptions,
17
15
  } from "./utils";
18
16
  import type {
19
17
  HostedButtonsComponent,
20
18
  HostedButtonsComponentProps,
21
19
  HostedButtonsInstance,
20
+ HostedButtonOptions,
22
21
  } from "./types";
23
22
 
24
23
  export const getHostedButtonsComponent = (): HostedButtonsComponent => {
25
24
  function HostedButtons({
26
25
  enableDPoP = false,
27
26
  hostedButtonId,
28
- fundingSources = [],
29
27
  }: HostedButtonsComponentProps): HostedButtonsInstance {
30
28
  const Buttons = getButtonsComponent();
31
29
  const render = async (selector) => {
32
30
  const merchantId = getMerchantID();
33
- const { html, htmlScript, style } = await getHostedButtonDetails({
31
+ const {
32
+ html,
33
+ htmlScript,
34
+ style,
35
+ version,
36
+ preferences,
37
+ buttonContainerId,
38
+ } = await getHostedButtonDetails({
34
39
  hostedButtonId,
35
- fundingSources,
36
40
  });
37
41
 
38
42
  const { onInit, onClick } = renderForm({
@@ -47,13 +51,14 @@ export const getHostedButtonsComponent = (): HostedButtonsComponent => {
47
51
  hostedButtonId,
48
52
  merchantId,
49
53
  });
54
+
50
55
  const onApprove = buildHostedButtonOnApprove({
51
56
  enableDPoP,
52
57
  hostedButtonId,
53
58
  merchantId,
54
59
  });
55
60
 
56
- const buttonOptions = {
61
+ const buttonOptions: HostedButtonOptions = {
57
62
  createOrder,
58
63
  hostedButtonId,
59
64
  merchantId,
@@ -63,30 +68,25 @@ export const getHostedButtonsComponent = (): HostedButtonsComponent => {
63
68
  style,
64
69
  };
65
70
 
66
- if (shouldRenderSDKButtons(fundingSources)) {
71
+ if (version === "2") {
67
72
  const { flexDirection } = getFlexDirection({ ...style });
68
73
 
69
- appendButtonContainer({ flexDirection, selector });
70
-
71
- // Only render 2 buttons max
72
- // This will be refactored in https://paypal.atlassian.net/browse/DTPPCPSDK-2112 when NCPS team updates their API response
73
- fundingSources.slice(0, 2).forEach((fundingSource, index) => {
74
- // $FlowFixMe
75
- const standaloneButton = Buttons({
76
- ...buttonOptions,
77
- fundingSource,
78
- style: {
79
- ...style,
80
- color: getButtonColor(style.color, fundingSource),
81
- },
82
- });
74
+ applyContainerStyles({ flexDirection, buttonContainerId });
83
75
 
84
- if (standaloneButton.isEligible()) {
85
- standaloneButton.render(
86
- index === 0 ? "#ncp-primary-button" : "#ncp-secondary-button"
87
- );
76
+ preferences?.buttonPreferences.forEach((fundingSource) => {
77
+ if (fundingSource === "default") {
78
+ const eligibleDefaultButtons = getDefaultButtonOptions(preferences);
79
+ renderDefaultButton({
80
+ eligibleDefaultButtons,
81
+ buttonContainerId,
82
+ buttonOptions,
83
+ });
88
84
  } else {
89
- getLogger().error(`ncps_standalone_${fundingSource}_ineligible`);
85
+ renderStandaloneButton({
86
+ fundingSource,
87
+ buttonContainerId,
88
+ buttonOptions,
89
+ });
90
90
  }
91
91
  });
92
92
  } else {
@@ -6,6 +6,8 @@ import { request } from "@krakenjs/belter/src";
6
6
 
7
7
  import { getButtonsComponent } from "../zoid/buttons";
8
8
 
9
+ import { renderDefaultButton, renderStandaloneButton } from "./utils";
10
+
9
11
  import { getHostedButtonsComponent } from ".";
10
12
 
11
13
  vi.mock("@krakenjs/belter/src", async () => {
@@ -31,44 +33,56 @@ vi.mock("../zoid/buttons", async () => {
31
33
  };
32
34
  });
33
35
 
34
- const getHostedButtonDetailsResponse = {
35
- body: {
36
- button_details: {
37
- link_variables: [
38
- {
39
- name: "shape",
40
- value: "rect",
41
- },
42
- {
43
- name: "layout",
44
- value: "vertical",
45
- },
46
- {
47
- name: "color",
48
- value: "gold",
49
- },
50
- {
51
- name: "button_text",
52
- value: "paypal",
53
- },
54
- {
55
- name: "button_type",
56
- value: "FIXED_PRICE",
57
- },
58
- {
59
- name: "tagline",
60
- value: "true",
61
- },
62
- {
63
- name: "height",
64
- value: "40",
65
- },
66
- ],
67
- },
36
+ vi.mock("./utils.js", async () => {
37
+ return {
38
+ ...(await vi.importActual("./utils.js")),
39
+ renderStandaloneButton: vi.fn(),
40
+ renderDefaultButton: vi.fn(),
41
+ };
42
+ });
43
+
44
+ const baseLinkVariables = [
45
+ {
46
+ name: "shape",
47
+ value: "rect",
48
+ },
49
+ {
50
+ name: "layout",
51
+ value: "vertical",
52
+ },
53
+ {
54
+ name: "color",
55
+ value: "gold",
56
+ },
57
+ {
58
+ name: "button_text",
59
+ value: "paypal",
60
+ },
61
+ {
62
+ name: "button_type",
63
+ value: "FIXED_PRICE",
68
64
  },
69
- };
65
+ {
66
+ name: "tagline",
67
+ value: "true",
68
+ },
69
+ {
70
+ name: "height",
71
+ value: "40",
72
+ },
73
+ ];
74
+
75
+ const hostedButtonId = "B1234567890";
76
+
77
+ describe("HostedButtons v1", () => {
78
+ const hostedButtonDetailsResponse = {
79
+ body: {
80
+ button_details: {
81
+ link_variables: baseLinkVariables,
82
+ },
83
+ },
84
+ };
70
85
 
71
- describe("HostedButtons", () => {
72
86
  test("paypal.Buttons calls getHostedButtonDetails and invokes v5 of the SDK", async () => {
73
87
  const Buttons = vi.fn(() => ({ render: vi.fn() }));
74
88
  // $FlowIssue
@@ -77,15 +91,15 @@ describe("HostedButtons", () => {
77
91
  // $FlowIssue
78
92
  request.mockImplementationOnce(() =>
79
93
  // eslint-disable-next-line compat/compat
80
- Promise.resolve(getHostedButtonDetailsResponse)
94
+ Promise.resolve(hostedButtonDetailsResponse)
81
95
  );
82
96
  await HostedButtons({
83
- hostedButtonId: "B1234567890",
84
- fundingSources: [],
97
+ hostedButtonId,
85
98
  }).render("#example");
99
+
86
100
  expect(Buttons).toHaveBeenCalledWith(
87
101
  expect.objectContaining({
88
- hostedButtonId: "B1234567890",
102
+ hostedButtonId,
89
103
  style: expect.objectContaining({ tagline: true }),
90
104
  })
91
105
  );
@@ -93,44 +107,6 @@ describe("HostedButtons", () => {
93
107
  expect.assertions(2);
94
108
  });
95
109
 
96
- describe("NCP V2", () => {
97
- beforeEach(() => {
98
- const containerId = "#container-id";
99
- const selector = document.createElement("div");
100
- selector.setAttribute("id", containerId.slice(1));
101
- vi.spyOn(document, "querySelector").mockReturnValue(selector);
102
- });
103
-
104
- test("paypal.Buttons calls getHostedButtonDetails, invokes v5 of the SDK", async () => {
105
- const renderMock = vi.fn();
106
-
107
- const Buttons = vi.fn(() => ({
108
- render: renderMock,
109
- isEligible: vi.fn(() => true),
110
- }));
111
- // $FlowIssue
112
- getButtonsComponent.mockImplementationOnce(() => Buttons);
113
- const HostedButtons = getHostedButtonsComponent();
114
- // $FlowIssue
115
- request.mockImplementationOnce(() =>
116
- // eslint-disable-next-line compat/compat
117
- Promise.resolve(getHostedButtonDetailsResponse)
118
- );
119
- await HostedButtons({
120
- hostedButtonId: "B1234567890",
121
- fundingSources: ["paypal", "venmo"],
122
- }).render("#example");
123
- expect(Buttons).toHaveBeenCalledWith(
124
- expect.objectContaining({
125
- hostedButtonId: "B1234567890",
126
- })
127
- );
128
- expect(Buttons).toHaveBeenCalledTimes(2);
129
- expect(renderMock).toHaveBeenCalledTimes(2);
130
- expect.assertions(3);
131
- });
132
- });
133
-
134
110
  test("only eligible buttons are rendered", async () => {
135
111
  const renderMock = vi.fn();
136
112
 
@@ -144,19 +120,19 @@ describe("HostedButtons", () => {
144
120
  // $FlowIssue
145
121
  request.mockImplementationOnce(() =>
146
122
  // eslint-disable-next-line compat/compat
147
- Promise.resolve(getHostedButtonDetailsResponse)
123
+ Promise.resolve(hostedButtonDetailsResponse)
148
124
  );
149
125
  await HostedButtons({
150
- hostedButtonId: "B1234567890",
151
- fundingSources: ["paypal", "venmo"],
126
+ hostedButtonId,
152
127
  }).render("#example");
128
+
153
129
  expect(Buttons).toHaveBeenCalledWith(
154
130
  expect.objectContaining({
155
- hostedButtonId: "B1234567890",
131
+ hostedButtonId,
156
132
  })
157
133
  );
158
- expect(Buttons).toHaveBeenCalledTimes(2);
159
- expect(renderMock).toHaveBeenCalledTimes(0);
134
+ expect(Buttons).toHaveBeenCalledTimes(1);
135
+ expect(renderMock).toHaveBeenCalledTimes(1);
160
136
  expect.assertions(3);
161
137
  });
162
138
 
@@ -207,11 +183,12 @@ describe("HostedButtons", () => {
207
183
  })
208
184
  );
209
185
  await HostedButtons({
210
- hostedButtonId: "B1234567890",
186
+ hostedButtonId,
211
187
  }).render("#example");
188
+
212
189
  expect(Buttons).toHaveBeenCalledWith(
213
190
  expect.objectContaining({
214
- hostedButtonId: "B1234567890",
191
+ hostedButtonId,
215
192
  style: expect.objectContaining({ tagline: false }),
216
193
  })
217
194
  );
@@ -220,4 +197,68 @@ describe("HostedButtons", () => {
220
197
  expect.assertions(3);
221
198
  });
222
199
  });
200
+
201
+ describe("HostedButtons v2", () => {
202
+ const hostedButtonDetailsResponse = {
203
+ body: {
204
+ version: "2",
205
+ button_details: {
206
+ link_variables: baseLinkVariables,
207
+ js_sdk_container_id: "spb-container",
208
+ preferences: {
209
+ button_preferences: ["paypal", "default"],
210
+ eligible_funding_methods: ["paypal", "venmo", "paylater"],
211
+ },
212
+ },
213
+ },
214
+ };
215
+
216
+ beforeEach(() => {
217
+ vi.restoreAllMocks();
218
+
219
+ const selector = document.createElement("div");
220
+ selector.id =
221
+ hostedButtonDetailsResponse.body.button_details.js_sdk_container_id;
222
+ vi.spyOn(document, "querySelector").mockReturnValue(selector);
223
+ });
224
+
225
+ test("paypal.HostedButtons calls renderStandaloneButton & renderDefaultButton accordingly", async () => {
226
+ const renderMock = vi.fn();
227
+
228
+ const Buttons = vi.fn(() => ({
229
+ render: renderMock,
230
+ isEligible: vi.fn(() => false),
231
+ }));
232
+ // $FlowIssue
233
+ getButtonsComponent.mockImplementation(() => Buttons);
234
+
235
+ const HostedButtons = getHostedButtonsComponent();
236
+
237
+ // $FlowIssue
238
+ request.mockImplementationOnce(() =>
239
+ // eslint-disable-next-line compat/compat
240
+ Promise.resolve(hostedButtonDetailsResponse)
241
+ );
242
+
243
+ await HostedButtons({
244
+ hostedButtonId,
245
+ }).render("#example");
246
+
247
+ expect(renderStandaloneButton).toHaveBeenCalledTimes(1);
248
+ expect(renderDefaultButton).toHaveBeenCalledTimes(1);
249
+
250
+ expect(renderStandaloneButton).toHaveBeenCalledWith(
251
+ expect.objectContaining({
252
+ fundingSource: "paypal",
253
+ })
254
+ );
255
+
256
+ expect(renderDefaultButton).toHaveBeenCalledWith(
257
+ expect.objectContaining({
258
+ eligibleDefaultButtons: ["venmo", "paylater"],
259
+ })
260
+ );
261
+ });
262
+ });
263
+
223
264
  /* eslint-enable no-restricted-globals, promise/no-native */
@@ -1,10 +1,23 @@
1
1
  /* @flow */
2
2
  /* eslint-disable no-restricted-globals, promise/no-native */
3
+ import { FUNDING } from "@paypal/sdk-constants/src";
3
4
 
4
5
  export type Color = string;
5
6
  export type FlexDirection = string;
6
7
  export type Layout = string;
7
8
 
9
+ export type CreateOrder = (data: {|
10
+ paymentSource: string,
11
+ |}) => Promise<string | void>;
12
+
13
+ export type OnApprove = (data: {|
14
+ orderID: string,
15
+ paymentSource: string,
16
+ |}) => Promise<mixed>;
17
+
18
+ type OnInit = (data: mixed, actions: mixed) => void;
19
+ type OnClick = (data: mixed, actions: mixed) => void;
20
+
8
21
  export type FundingSources = string;
9
22
  export interface GetFlexDirection {
10
23
  flexDirection: FlexDirection;
@@ -19,6 +32,61 @@ export interface BuildButtonContainerArgs {
19
32
  selector: string | HTMLElement;
20
33
  }
21
34
 
35
+ export type ApplyButtonStylesProps = {|
36
+ flexDirection: FlexDirection,
37
+ buttonContainerId: string,
38
+ |};
39
+
40
+ export type HostedButtonStyles = {|
41
+ layout: string,
42
+ shape: string,
43
+ color: string,
44
+ label: string,
45
+ height: ?number,
46
+ tagline: boolean,
47
+ |};
48
+
49
+ export type HostedButtonOptions = {|
50
+ createOrder: CreateOrder,
51
+ onApprove: OnApprove,
52
+ onClick: OnClick,
53
+ onInit: OnInit,
54
+ style: HostedButtonStyles,
55
+ hostedButtonId: string,
56
+ merchantId?: string,
57
+ |};
58
+
59
+ export type ButtonPreferences = $ReadOnlyArray<
60
+ $Values<typeof FUNDING> | "default"
61
+ >;
62
+
63
+ export type HostedButtonPreferences = {|
64
+ buttonPreferences: ButtonPreferences,
65
+ eligibleFundingMethods: $ReadOnlyArray<$Values<typeof FUNDING>>,
66
+ |};
67
+
68
+ export type NcpResponsePreferences = {|
69
+ button_preferences: ButtonPreferences,
70
+ eligible_funding_methods: $ReadOnlyArray<$Values<typeof FUNDING>>,
71
+ |};
72
+
73
+ export type GetButtonsProps = {|
74
+ fundingSource: FundingSources,
75
+ buttonOptions: HostedButtonOptions,
76
+ |};
77
+
78
+ export type RenderStandaloneButtonProps = {|
79
+ fundingSource: FundingSources,
80
+ buttonContainerId: string,
81
+ buttonOptions: HostedButtonOptions,
82
+ |};
83
+
84
+ export type RenderDefaultButtonProps = {|
85
+ eligibleDefaultButtons: $ReadOnlyArray<FundingSources>,
86
+ buttonContainerId: string,
87
+ buttonOptions: HostedButtonOptions,
88
+ |};
89
+
22
90
  export type HostedButtonsComponentProps = {|
23
91
  hostedButtonId: string,
24
92
  fundingSources?: $ReadOnlyArray<FundingSources>,
@@ -34,27 +102,13 @@ export type HostedButtonsInstance = {|
34
102
  render: (string | HTMLElement) => Promise<void>,
35
103
  |};
36
104
 
37
- export type EligibleHostedButtons = "paypal" | "venmo" | "paylater";
38
-
39
- export type HostedButtonPreferences = {|
40
- buttonPreferences: $ReadOnlyArray<EligibleHostedButtons & "default">,
41
- eligibleFundingMethods: $ReadOnlyArray<EligibleHostedButtons>,
42
- |};
43
-
44
105
  export type HostedButtonDetailsParams =
45
106
  (HostedButtonsComponentProps) => Promise<{|
46
107
  html: string,
47
108
  htmlScript: string,
48
- style: {|
49
- layout: string,
50
- shape: string,
51
- color: string,
52
- label: string,
53
- height: ?number,
54
- tagline: boolean,
55
- |},
109
+ style: HostedButtonStyles,
56
110
  version: ?string,
57
- buttonContainerId: ?string,
111
+ buttonContainerId: string,
58
112
  preferences?: HostedButtonPreferences,
59
113
  |}>;
60
114
 
@@ -63,15 +117,6 @@ export type ButtonVariables = $ReadOnlyArray<{|
63
117
  value: string,
64
118
  |}>;
65
119
 
66
- export type CreateOrder = (data: {|
67
- paymentSource: string,
68
- |}) => Promise<string | void>;
69
-
70
- export type OnApprove = (data: {|
71
- orderID: string,
72
- paymentSource: string,
73
- |}) => Promise<mixed>;
74
-
75
120
  export type CreateAccessToken = ({|
76
121
  clientId: string,
77
122
  enableDPoP?: boolean,
@@ -86,8 +131,8 @@ export type RenderForm = ({|
86
131
  htmlScript: string,
87
132
  selector: string | HTMLElement,
88
133
  |}) => {|
89
- onInit: (data: mixed, actions: mixed) => void,
90
- onClick: (data: mixed, actions: mixed) => void,
134
+ onInit: OnInit,
135
+ onClick: OnClick,
91
136
  |};
92
137
 
93
138
  /* eslint-enable no-restricted-globals, promise/no-native */