@paypal/checkout-components
Advanced tools
Comparing version 5.0.309-alpha-7a6ed47.0 to 5.0.309-alpha-fcfe679.0
@@ -35,6 +35,5 @@ /* eslint import/no-commonjs: off, flowtype/require-valid-file-annotation: off, flowtype/require-return-type: off */ | ||
__PAYMENT_FIELDS__: "/altpayfields", | ||
__MESSAGE_MODAL__: | ||
"https://www.paypalobjects.com/upstream/bizcomponents/js/modal.js", | ||
__MESSAGE_MODAL__: "https://www.paypalobjects.com/upstream/bizcomponents/js/modal.js", | ||
}, | ||
}, | ||
}; |
{ | ||
"name": "@paypal/checkout-components", | ||
"version": "5.0.309-alpha-7a6ed47.0", | ||
"version": "5.0.309-alpha-fcfe679.0", | ||
"description": "PayPal Checkout components, for integrating checkout products.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
/* @flow */ | ||
import { getLogger } from "@paypal/sdk-client/src"; | ||
import { getButtonsComponent } from "../zoid/buttons"; | ||
@@ -13,6 +11,2 @@ | ||
getMerchantID, | ||
shouldRenderSDKButtons, | ||
getFlexDirection, | ||
appendButtonContainer, | ||
getButtonColor, | ||
} from "./utils"; | ||
@@ -29,3 +23,2 @@ import type { | ||
hostedButtonId, | ||
fundingSources = [], | ||
}: HostedButtonsComponentProps): HostedButtonsInstance { | ||
@@ -37,3 +30,2 @@ const Buttons = getButtonsComponent(); | ||
hostedButtonId, | ||
fundingSources, | ||
}); | ||
@@ -48,56 +40,20 @@ | ||
const createOrder = buildHostedButtonCreateOrder({ | ||
enableDPoP, | ||
// $FlowFixMe | ||
Buttons({ | ||
hostedButtonId, | ||
merchantId, | ||
}); | ||
const onApprove = buildHostedButtonOnApprove({ | ||
enableDPoP, | ||
hostedButtonId, | ||
merchantId, | ||
}); | ||
const buttonOptions = { | ||
createOrder, | ||
hostedButtonId, | ||
merchantId, | ||
onApprove, | ||
style, | ||
onInit, | ||
onClick, | ||
onInit, | ||
style, | ||
}; | ||
if (shouldRenderSDKButtons(fundingSources)) { | ||
const { flexDirection } = getFlexDirection({ ...style }); | ||
appendButtonContainer({ flexDirection, selector }); | ||
// Only render 2 buttons max | ||
// This will be refactored in https://paypal.atlassian.net/browse/DTPPCPSDK-2112 when NCPS team updates their API response | ||
fundingSources.slice(0, 2).forEach((fundingSource, index) => { | ||
// $FlowFixMe | ||
const standaloneButton = Buttons({ | ||
...buttonOptions, | ||
fundingSource, | ||
style: { | ||
...style, | ||
color: getButtonColor(style.color, fundingSource), | ||
}, | ||
}); | ||
if (standaloneButton.isEligible()) { | ||
standaloneButton.render( | ||
index === 0 ? "#ncp-primary-button" : "#ncp-secondary-button" | ||
); | ||
} else { | ||
getLogger().error(`ncps_standalone_${fundingSource}_ineligible`); | ||
} | ||
}); | ||
} else { | ||
// V1 Experience | ||
// $FlowFixMe | ||
Buttons(buttonOptions).render(selector); | ||
} | ||
createOrder: buildHostedButtonCreateOrder({ | ||
enableDPoP, | ||
hostedButtonId, | ||
merchantId, | ||
}), | ||
onApprove: buildHostedButtonOnApprove({ | ||
enableDPoP, | ||
hostedButtonId, | ||
merchantId, | ||
}), | ||
}).render(selector); | ||
}; | ||
return { | ||
@@ -104,0 +60,0 @@ render, |
@@ -76,3 +76,2 @@ /* @flow */ | ||
hostedButtonId: "B1234567890", | ||
fundingSources: [], | ||
}).render("#example"); | ||
@@ -84,73 +83,6 @@ expect(Buttons).toHaveBeenCalledWith( | ||
); | ||
expect(Buttons).toHaveBeenCalledTimes(1); | ||
expect.assertions(2); | ||
expect.assertions(1); | ||
}); | ||
}); | ||
describe("NCP V2", () => { | ||
beforeEach(() => { | ||
const containerId = "#container-id"; | ||
const selector = document.createElement("div"); | ||
selector.setAttribute("id", containerId.slice(1)); | ||
vi.spyOn(document, "querySelector").mockReturnValue(selector); | ||
}); | ||
test("paypal.Buttons calls getHostedButtonDetails, invokes v5 of the SDK", async () => { | ||
const renderMock = vi.fn(); | ||
const Buttons = vi.fn(() => ({ | ||
render: renderMock, | ||
isEligible: vi.fn(() => true), | ||
})); | ||
// $FlowIssue | ||
getButtonsComponent.mockImplementationOnce(() => Buttons); | ||
const HostedButtons = getHostedButtonsComponent(); | ||
// $FlowIssue | ||
request.mockImplementationOnce(() => | ||
// eslint-disable-next-line compat/compat | ||
Promise.resolve(getHostedButtonDetailsResponse) | ||
); | ||
await HostedButtons({ | ||
hostedButtonId: "B1234567890", | ||
fundingSources: ["paypal", "venmo"], | ||
}).render("#example"); | ||
expect(Buttons).toHaveBeenCalledWith( | ||
expect.objectContaining({ | ||
hostedButtonId: "B1234567890", | ||
}) | ||
); | ||
expect(Buttons).toHaveBeenCalledTimes(2); | ||
expect(renderMock).toHaveBeenCalledTimes(2); | ||
expect.assertions(3); | ||
}); | ||
}); | ||
test("only eligible buttons are rendered", async () => { | ||
const renderMock = vi.fn(); | ||
const Buttons = vi.fn(() => ({ | ||
render: renderMock, | ||
isEligible: vi.fn(() => false), | ||
})); | ||
// $FlowIssue | ||
getButtonsComponent.mockImplementationOnce(() => Buttons); | ||
const HostedButtons = getHostedButtonsComponent(); | ||
// $FlowIssue | ||
request.mockImplementationOnce(() => | ||
// eslint-disable-next-line compat/compat | ||
Promise.resolve(getHostedButtonDetailsResponse) | ||
); | ||
await HostedButtons({ | ||
hostedButtonId: "B1234567890", | ||
fundingSources: ["paypal", "venmo"], | ||
}).render("#example"); | ||
expect(Buttons).toHaveBeenCalledWith( | ||
expect.objectContaining({ | ||
hostedButtonId: "B1234567890", | ||
}) | ||
); | ||
expect(Buttons).toHaveBeenCalledTimes(2); | ||
expect(renderMock).toHaveBeenCalledTimes(0); | ||
expect.assertions(3); | ||
}); | ||
}); | ||
/* eslint-enable no-restricted-globals, promise/no-native */ |
/* @flow */ | ||
/* eslint-disable no-restricted-globals, promise/no-native */ | ||
export type Color = string; | ||
export type FlexDirection = string; | ||
export type Layout = string; | ||
export type FundingSources = string; | ||
export interface GetFlexDirection { | ||
flexDirection: FlexDirection; | ||
} | ||
export interface GetFlexDirectionArgs { | ||
layout: Layout; | ||
} | ||
export interface BuildButtonContainerArgs { | ||
flexDirection: FlexDirection; | ||
selector: string | HTMLElement; | ||
} | ||
export type HostedButtonsComponentProps = {| | ||
hostedButtonId: string, | ||
fundingSources: $ReadOnlyArray<FundingSources>, | ||
|}; | ||
@@ -26,0 +7,0 @@ |
@@ -20,7 +20,2 @@ /* @flow */ | ||
RenderForm, | ||
GetFlexDirectionArgs, | ||
GetFlexDirection, | ||
BuildButtonContainerArgs, | ||
Color, | ||
FundingSources, | ||
} from "./types"; | ||
@@ -39,6 +34,5 @@ | ||
export const getMerchantID = (): string | void => { | ||
// The SDK supports Multi-Seller Payments (MSP, i.e sending multiple merchant IDs), but hosted buttons | ||
// does not support this. Only one merchant id can be passed as a query parameter to the SDK script | ||
// The SDK supports mutiple merchant IDs, but hosted buttons only | ||
// have one merchant id as a query parameter to the SDK script. | ||
// https://github.com/paypal/paypal-sdk-client/blob/c58e35f8f7adbab76523eb25b9c10543449d2d29/src/script.js#L144 | ||
// https://developer.paypal.com/docs/multiparty/checkout/multiseller-payments/ | ||
const merchantIds = getSDKMerchantID(); | ||
@@ -108,10 +102,2 @@ if (merchantIds.length > 1) { | ||
export function getElementFromSelector( | ||
selector: string | HTMLElement | ||
): HTMLElement | null { | ||
return typeof selector === "string" | ||
? document.querySelector(selector) | ||
: selector; | ||
} | ||
/** | ||
@@ -127,3 +113,4 @@ * Attaches form fields (html) to the given selector, and | ||
}) => { | ||
const elm = getElementFromSelector(selector); | ||
const elm = | ||
typeof selector === "string" ? document.querySelector(selector) : selector; | ||
if (elm) { | ||
@@ -245,79 +232,1 @@ elm.innerHTML = html + htmlScript; | ||
}; | ||
export function getFlexDirection({ | ||
layout, | ||
}: GetFlexDirectionArgs): GetFlexDirection { | ||
return { flexDirection: layout === "horizontal" ? "row" : "column" }; | ||
} | ||
export function getButtonColor( | ||
color: Color, | ||
fundingSource: FundingSources | ||
): Color { | ||
const colorMap = { | ||
gold: { | ||
paypal: "gold", | ||
venmo: "blue", | ||
paylater: "gold", | ||
}, | ||
blue: { | ||
paypal: "blue", | ||
venmo: "silver", | ||
paylater: "blue", | ||
}, | ||
black: { | ||
paypal: "black", | ||
venmo: "black", | ||
paylater: "black", | ||
}, | ||
white: { | ||
paypal: "white", | ||
venmo: "white", | ||
paylater: "white", | ||
}, | ||
silver: { | ||
paypal: "silver", | ||
venmo: "blue", | ||
paylater: "silver", | ||
}, | ||
}; | ||
return colorMap[color][fundingSource]; | ||
} | ||
export function shouldRenderSDKButtons( | ||
fundingSources: $ReadOnlyArray<FundingSources> | ||
): boolean { | ||
return Boolean(fundingSources.length); | ||
} | ||
export function appendButtonContainer({ | ||
flexDirection, | ||
selector, | ||
}: BuildButtonContainerArgs) { | ||
const elm = getElementFromSelector(selector); | ||
if (!elm) { | ||
throw new Error("PayPal button container selector was not found"); | ||
} | ||
const buttonContainer = document.createElement("div"); | ||
buttonContainer.setAttribute( | ||
"style", | ||
`display: flex; flex-wrap: nowrap; gap: 16px; max-width: 750px; flex-direction: ${flexDirection}` | ||
); | ||
const primaryButton = document.createElement("div"); | ||
primaryButton.setAttribute("id", `ncp-primary-button`); | ||
primaryButton.setAttribute("style", "flex-grow: 1"); | ||
const secondaryButton = document.createElement("div"); | ||
secondaryButton.setAttribute("id", `ncp-secondary-button`); | ||
secondaryButton.setAttribute("style", "flex-grow: 1"); | ||
buttonContainer.appendChild(primaryButton); | ||
buttonContainer.appendChild(secondaryButton); | ||
elm?.appendChild(buttonContainer); | ||
} |
@@ -11,7 +11,2 @@ /* @flow */ | ||
getHostedButtonDetails, | ||
getFlexDirection, | ||
getButtonColor, | ||
shouldRenderSDKButtons, | ||
appendButtonContainer, | ||
getElementFromSelector, | ||
} from "./utils"; | ||
@@ -86,3 +81,2 @@ | ||
hostedButtonId, | ||
fundingSources: [], | ||
}).then(({ style }) => { | ||
@@ -320,97 +314,2 @@ expect(style).toEqual({ | ||
test("getFlexDirection", () => { | ||
expect(getFlexDirection({ layout: "horizontal" })).toStrictEqual({ | ||
flexDirection: "row", | ||
}); | ||
expect(getFlexDirection({ layout: "vertical" })).toStrictEqual({ | ||
flexDirection: "column", | ||
}); | ||
}); | ||
test("getButtonColor", () => { | ||
const colors = ["gold", "blue", "silver", "white", "black"]; | ||
const fundingSources = ["paypal", "venmo", "paylater"]; | ||
const colorMap = { | ||
gold: { | ||
paypal: "gold", | ||
venmo: "blue", | ||
paylater: "gold", | ||
}, | ||
blue: { | ||
paypal: "blue", | ||
venmo: "silver", | ||
paylater: "blue", | ||
}, | ||
black: { | ||
paypal: "black", | ||
venmo: "black", | ||
paylater: "black", | ||
}, | ||
white: { | ||
paypal: "white", | ||
venmo: "white", | ||
paylater: "white", | ||
}, | ||
silver: { | ||
paypal: "silver", | ||
venmo: "blue", | ||
paylater: "silver", | ||
}, | ||
}; | ||
colors.forEach((color) => { | ||
fundingSources.forEach((fundingSource) => { | ||
expect(getButtonColor(color, fundingSource)).toBe( | ||
colorMap[color][fundingSource] | ||
); | ||
}); | ||
}); | ||
}); | ||
test("shouldRenderSDKButtons", () => { | ||
expect(shouldRenderSDKButtons([])).toBe(false); | ||
expect(shouldRenderSDKButtons(["paypal"])).toBe(true); | ||
expect(shouldRenderSDKButtons(["paypal", "venmo"])).toBe(true); | ||
}); | ||
test("buildButtonContainer", () => { | ||
const containerId = "#container-id"; | ||
const selector = document.createElement("div"); | ||
selector.setAttribute("id", containerId.slice(1)); | ||
vi.spyOn(document, "querySelector").mockReturnValueOnce(selector); | ||
expect(() => | ||
appendButtonContainer({ flexDirection: "row", selector: containerId }) | ||
).not.toThrow(); | ||
expect(() => | ||
appendButtonContainer({ flexDirection: "row", selector }) | ||
).not.toThrow(); | ||
expect(() => | ||
appendButtonContainer({ | ||
flexDirection: "row", | ||
selector: `${containerId}-not-found`, | ||
}) | ||
).toThrow("PayPal button container selector was not found"); | ||
}); | ||
test("getElementFromSelector", () => { | ||
const containerId = "#container-id"; | ||
const selector = document.createElement("div"); | ||
selector.setAttribute("id", containerId.slice(1)); | ||
const mockQuerySelector = vi | ||
.spyOn(document, "querySelector") | ||
.mockReturnValueOnce(selector); | ||
expect(getElementFromSelector(containerId)).toBe(selector); | ||
expect(getElementFromSelector(selector)).toBe(selector); | ||
expect(mockQuerySelector).toBeCalledTimes(1); | ||
expect(mockQuerySelector).toHaveBeenCalledWith(containerId); | ||
}); | ||
/* eslint-enable no-restricted-globals, promise/no-native */ |
@@ -433,3 +433,3 @@ /* eslint-disable eslint-comments/disable-enable-pair */ | ||
amount?: number, | ||
offer?: $Values<typeof MESSAGE_OFFER> | string, | ||
offer?: $ReadOnlyArray<$Values<typeof MESSAGE_OFFER>>, | ||
color: $Values<typeof MESSAGE_COLOR>, | ||
@@ -745,2 +745,3 @@ position: $Values<typeof MESSAGE_POSITION>, | ||
amount, | ||
offer, | ||
color = MESSAGE_COLOR.BLACK, | ||
@@ -750,3 +751,3 @@ position, | ||
} = message; | ||
let offer = message.offer; | ||
if (typeof amount !== "undefined") { | ||
@@ -767,10 +768,7 @@ if (typeof amount !== "number") { | ||
if (!Array.isArray(offer)) { | ||
if (typeof offer !== "string") { | ||
throw new TypeError( | ||
`Expected message.offer to be an array of strings, got: ${String( | ||
offer | ||
)}` | ||
); | ||
} | ||
offer = offer.split(","); | ||
throw new TypeError( | ||
`Expected message.offer to be an array of strings, got: ${String( | ||
offer | ||
)}` | ||
); | ||
} | ||
@@ -783,3 +781,2 @@ const invalidOffers = offer.filter( | ||
} | ||
offer = offer.join(","); | ||
} | ||
@@ -786,0 +783,0 @@ |
@@ -746,3 +746,7 @@ /* @flow */ | ||
const modalInstance = await getModal(clientID, merchantID); | ||
const modalInstance = await getModal( | ||
clientID, | ||
merchantID, | ||
buttonSessionID | ||
); | ||
return modalInstance?.show({ | ||
@@ -765,4 +769,4 @@ amount, | ||
// lazy loads the modal, to be memoized and executed onMessageClick | ||
const { clientID, merchantID } = props; | ||
return getModal(clientID, merchantID); | ||
const { buttonSessionID, clientID, merchantID } = props; | ||
return getModal(clientID, merchantID, buttonSessionID); | ||
}; | ||
@@ -769,0 +773,0 @@ }, |
@@ -18,3 +18,2 @@ /* @flow */ | ||
} from "@krakenjs/belter/src"; | ||
import { FUNDING } from "@paypal/sdk-constants/src"; | ||
import { | ||
@@ -30,2 +29,3 @@ getEnableFunding, | ||
} from "@paypal/sdk-client/src"; | ||
import { FUNDING, FPTI_KEY } from "@paypal/sdk-constants/src"; | ||
import { getRefinedFundingEligibility } from "@paypal/funding-components/src"; | ||
@@ -378,4 +378,5 @@ | ||
clientID: string, | ||
merchantID: $ReadOnlyArray<string> | void | ||
) => Object = memoize(async (clientID, merchantID) => { | ||
merchantID: $ReadOnlyArray<string> | void, | ||
buttonSessionID: string | ||
) => Object = memoize(async (clientID, merchantID, buttonSessionID) => { | ||
try { | ||
@@ -401,2 +402,36 @@ const namespace = getNamespace(); | ||
return window[namespace].MessagesModal({ | ||
onReady: () => | ||
getLogger() | ||
.info("button_message_modal_render") | ||
.track({ | ||
[FPTI_KEY.TRANSITION]: "button_message_modal_render", | ||
[FPTI_KEY.STATE]: "BUTTON_MESSAGE", | ||
[FPTI_KEY.BUTTON_SESSION_UID]: buttonSessionID, | ||
[FPTI_KEY.CONTEXT_ID]: buttonSessionID, | ||
[FPTI_KEY.CONTEXT_TYPE]: "button_session_id", | ||
[FPTI_KEY.EVENT_NAME]: "modal_render", | ||
}), | ||
onClick: () => | ||
getLogger() | ||
.info("button_message_modal_click") | ||
.track({ | ||
[FPTI_KEY.TRANSITION]: "button_message_modal_click", | ||
[FPTI_KEY.STATE]: "BUTTON_MESSAGE", | ||
[FPTI_KEY.BUTTON_SESSION_UID]: buttonSessionID, | ||
[FPTI_KEY.CONTEXT_ID]: buttonSessionID, | ||
[FPTI_KEY.CONTEXT_TYPE]: "button_session_id", | ||
[FPTI_KEY.EVENT_NAME]: "modal_click", | ||
}), | ||
onApply: () => | ||
getLogger() | ||
.info("button_message_modal_apply") | ||
.track({ | ||
[FPTI_KEY.TRANSITION]: "button_message_modal_apply", | ||
[FPTI_KEY.STATE]: "BUTTON_MESSAGE", | ||
[FPTI_KEY.BUTTON_SESSION_UID]: buttonSessionID, | ||
[FPTI_KEY.CONTEXT_ID]: buttonSessionID, | ||
[FPTI_KEY.CONTEXT_TYPE]: "button_session_id", | ||
[FPTI_KEY.EVENT_NAME]: "modal_apply", | ||
}), | ||
buttonSessionId: buttonSessionID, | ||
account: `client-id:${clientID}`, | ||
@@ -403,0 +438,0 @@ merchantId: merchantID?.join(",") || undefined, |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1159907
15164