Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/funding/common.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export type LabelOptions = {|
tagline: ?boolean,
content: ?ContentType,
experiment?: Experiment,
shouldApplyPayNowOrLaterLabel?: boolean,
|};

export type WalletLabelOptions = {|
Expand Down
9 changes: 9 additions & 0 deletions src/funding/content.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export type ContentMap = {
Donate?: ({|
logo: ChildType,
|}) => ChildType /** Not available in `tr` language **/,
PayNowOrLater?: ({| logo: ChildType |}) => ChildType,
|},
};

Expand Down Expand Up @@ -379,6 +380,14 @@ export const componentContent: ContentMap = {
</Text>
</Fragment>
),
PayNowOrLater: ({ logo }) => (
<Fragment>
{logo}
<Text animate optional>
Pay Now or Later
</Text>
</Fragment>
),
},
es: {
Checkout: ({ logo }) => (
Expand Down
11 changes: 11 additions & 0 deletions src/funding/paypal/template.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,17 @@ function ButtonPersonalization(opts: LabelOptions): ?ChildType {
}

export function Label(opts: LabelOptions): ChildType {
const {
logo,
locale: { lang },
shouldApplyPayNowOrLaterLabel,
} = opts;

const { PayNowOrLater } = componentContent[lang];
if (shouldApplyPayNowOrLaterLabel && PayNowOrLater) {
return <PayNowOrLater logo={logo} />;
}

return (
<Fragment>
<BasicLabel {...opts} />
Expand Down
1 change: 1 addition & 0 deletions src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export type Experiment = {|
venmoEnableWebOnNonNativeBrowser?: boolean,
spbEagerOrderCreation?: boolean,
paypalCreditButtonCreateVaultSetupTokenExists?: boolean,
isPaylaterCobrandedLabelEnabled?: boolean,
|};

export type Requires = {|
Expand Down
13 changes: 12 additions & 1 deletion src/ui/buttons/button.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,13 @@ export function Button({
const colors = fundingConfig.colors;
const secondaryColors = fundingConfig.secondaryColors || {};

let { color, period, label, shouldApplyRebrandedStyles } = style;
let {
color,
period,
label,
shouldApplyRebrandedStyles,
shouldApplyPayNowOrLaterLabel,
} = style;

// if no color option is passed in via style props
if (color === "" || typeof color === "undefined") {
Expand Down Expand Up @@ -189,6 +195,10 @@ export function Button({
})
: fundingConfig.labelText || fundingSource;

if (shouldApplyPayNowOrLaterLabel) {
labelText = "PayPal Pay Now or Later";
}

if (!showPayLabel && instrument?.vendor && instrument.label) {
labelText = instrument.secondaryInstruments
? `${instrument.secondaryInstruments[0].type} & ${instrument.vendor} ${instrument.label}`
Expand Down Expand Up @@ -234,6 +244,7 @@ export function Button({
tagline={tagline}
content={content}
experiment={experiment}
shouldApplyPayNowOrLaterLabel={shouldApplyPayNowOrLaterLabel}
/>
);

Expand Down
26 changes: 26 additions & 0 deletions src/ui/buttons/props.js
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,8 @@ export type ButtonStyle = {|
borderRadius?: number,
shouldApplyRebrandedStyles: boolean,
isButtonColorABTestMerchant: boolean,
isPayNowOrLaterLabelEligible: boolean,
shouldApplyPayNowOrLaterLabel: boolean,
|};

export type ButtonStyleInputs = {|
Expand Down Expand Up @@ -970,6 +972,25 @@ export function getButtonColor({
}
}

export function getCobrandedBNPLLabelFlags(props: ?ButtonPropsInputs): {|
isPayNowOrLaterLabelEligible: boolean,
shouldApplyPayNowOrLaterLabel: boolean,
|} {
const label = props?.style?.label;
const isPayNowOrLaterLabelEligible = Boolean(
props?.experiment?.isPaylaterCobrandedLabelEnabled &&
(props?.fundingSource === FUNDING.PAYPAL ||
props?.fundingSource === undefined) &&
props?.fundingEligibility?.paylater?.eligible &&
(label === undefined || label === BUTTON_LABEL.PAYPAL)
);

// All eligible sessions are treatment for now; future: add randomization here
const shouldApplyPayNowOrLaterLabel = isPayNowOrLaterLabelEligible;

return { isPayNowOrLaterLabelEligible, shouldApplyPayNowOrLaterLabel };
}

const getDefaultButtonPropsInput = (): ButtonPropsInputs => {
return {};
};
Expand Down Expand Up @@ -1119,6 +1140,9 @@ export function normalizeButtonStyle(
}
}

const { isPayNowOrLaterLabelEligible, shouldApplyPayNowOrLaterLabel } =
getCobrandedBNPLLabelFlags(props);

return {
label,
layout,
Expand All @@ -1133,6 +1157,8 @@ export function normalizeButtonStyle(
borderRadius,
shouldApplyRebrandedStyles,
isButtonColorABTestMerchant,
isPayNowOrLaterLabelEligible,
shouldApplyPayNowOrLaterLabel,
};
}

Expand Down
83 changes: 83 additions & 0 deletions src/ui/buttons/props.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
hasInvalidScriptOptionsForFullRedesign,
determineRandomButtonColor,
getColorABTestFromStorage,
getCobrandedBNPLLabelFlags,
} from "./props";

describe("getColorABTestFromStorage", () => {
Expand Down Expand Up @@ -864,3 +865,85 @@ describe("HideSubmitButtonProps type validation", () => {
expect(validButtonProps.hideSubmitButtonForCardForm).toBe(false);
});
});

describe("getCobrandedBNPLLabelFlags", () => {
// $FlowFixMe - test object intentionally omits non-relevant ButtonPropsInputs fields
const eligibleProps = {
fundingSource: FUNDING.PAYPAL,
fundingEligibility: {
paylater: { eligible: true },
},
experiment: { isPaylaterCobrandedLabelEnabled: true },
style: {},
};

it("should return true when all conditions are met", () => {
const { isPayNowOrLaterLabelEligible, shouldApplyPayNowOrLaterLabel } =
// $FlowFixMe
getCobrandedBNPLLabelFlags(eligibleProps);

expect(isPayNowOrLaterLabelEligible).toBe(true);
expect(shouldApplyPayNowOrLaterLabel).toBe(true);
});

it("should return false when experiment flag is disabled", () => {
const { isPayNowOrLaterLabelEligible } =
// $FlowFixMe
getCobrandedBNPLLabelFlags({
...eligibleProps,
experiment: { isPaylaterCobrandedLabelEnabled: false },
});

expect(isPayNowOrLaterLabelEligible).toBe(false);
});

it("should return false when paylater is not eligible", () => {
const { isPayNowOrLaterLabelEligible } =
// $FlowFixMe
getCobrandedBNPLLabelFlags({
...eligibleProps,
fundingEligibility: { paylater: { eligible: false } },
});

expect(isPayNowOrLaterLabelEligible).toBe(false);
});

it("should return false when fundingSource is not PAYPAL or undefined", () => {
const { isPayNowOrLaterLabelEligible } =
// $FlowFixMe
getCobrandedBNPLLabelFlags({
...eligibleProps,
fundingSource: FUNDING.VENMO,
});

expect(isPayNowOrLaterLabelEligible).toBe(false);
});

it("should return false when a non-paypal label is set", () => {
const { isPayNowOrLaterLabelEligible } =
// $FlowFixMe
getCobrandedBNPLLabelFlags({
...eligibleProps,
style: { label: "checkout" },
});

expect(isPayNowOrLaterLabelEligible).toBe(false);
});

it("should return true when label is explicitly set to paypal", () => {
const { isPayNowOrLaterLabelEligible } =
// $FlowFixMe
getCobrandedBNPLLabelFlags({
...eligibleProps,
style: { label: "paypal" },
});

expect(isPayNowOrLaterLabelEligible).toBe(true);
});

it("should return false when props is null", () => {
const { isPayNowOrLaterLabelEligible } = getCobrandedBNPLLabelFlags(null);

expect(isPayNowOrLaterLabelEligible).toBe(false);
});
});