-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ASUB-7841 Subscriptions Paywall - migrating from v1 to v2 (#1802)
* Subscriptions Paywall - Arc block migrating from v1 to v2 * PR feedback * fix test --------- Co-authored-by: Matthew Kim <[email protected]>
- Loading branch information
1 parent
4fbf36a
commit 945fbe3
Showing
16 changed files
with
1,693 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
109 changes: 109 additions & 0 deletions
109
blocks/subscriptions-block/components/PaywallOffer/index.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import React, { useEffect, useRef, useState } from "react"; | ||
import { isServerSide } from "@wpmedia/arc-themes-components"; | ||
import isUrl from "is-url"; | ||
import useOffer from "../useOffer"; | ||
import SubscriptionOverlay from "../SubscriptionOverlay"; | ||
import SubscriptionDialog from "../SubscriptionDialog"; | ||
|
||
const isPaywallCampaignURL = (payWallCode) => payWallCode && isUrl(payWallCode); | ||
|
||
const PaywallOffer = ({ | ||
isLoggedIn, | ||
actionText, | ||
actionUrl, | ||
campaignCode = null, | ||
displayMode, | ||
linkPrompt, | ||
linkText, | ||
linkUrl, | ||
reasonPrompt, | ||
usePortal = true, | ||
className | ||
}) => { | ||
// the paywall code (otherwise known as a campaign code) | ||
const [payWallCode, setPayWallCode] = useState(); | ||
|
||
const { offer, fetchOffer } = useOffer({ campaignCode }); | ||
|
||
/** | ||
* payWallOffer is the most updated offer that is returned from | ||
* the fetchOffer method in the useOffer hook. | ||
* We can't use the offer object that is returned from the useOffer | ||
* directly as that causes a recursive call to the second useEffect. | ||
* | ||
* When the payWallOffer is updated in the second hook it's current | ||
* value will be applied to the selectedOffer state to update the dom. | ||
*/ | ||
const payWallOffer = useRef(offer); | ||
|
||
const [selectedOffer, setSelectedOffer] = useState(payWallOffer.current); | ||
|
||
/** | ||
* If campaignCode is passed in as a prop, | ||
* use that. Otherwise use what is returned from | ||
* usePaywall hook and at the very least, set it | ||
* to "default" | ||
*/ | ||
useEffect(() => { | ||
const campaign = campaignCode || "default"; | ||
setPayWallCode(campaign); | ||
}, [campaignCode]); | ||
|
||
// This will grab the offer corresponding to the paywall code | ||
useEffect(() => { | ||
const fetchNewOffer = async () => { | ||
payWallOffer.current = await fetchOffer(payWallCode); | ||
if (payWallOffer.current) { | ||
setSelectedOffer(payWallOffer.current); | ||
} | ||
}; | ||
if ( | ||
payWallCode && | ||
!isUrl(payWallCode) && | ||
(!payWallOffer.current || payWallOffer.current.pw !== payWallCode) | ||
) { | ||
fetchNewOffer(); | ||
} | ||
return () => { | ||
payWallOffer.current = null; | ||
}; | ||
}, [payWallCode, fetchOffer]); | ||
|
||
/** | ||
* Return null if server side, not paywalled, doesn't have an offer to show | ||
* or if the user is logged in. | ||
*/ | ||
if (isServerSide() || !selectedOffer) { | ||
return null; | ||
} | ||
|
||
/** | ||
* Need to determine the campaign code. | ||
* First we see if we have been provided with a campaignCode prop. | ||
* If not then we check if the payWallCode is not a url, if its | ||
* not, then we use that. If it a url (Why would it be a URL??) then | ||
* we just set the campaign code to "default" | ||
*/ | ||
const campaign = campaignCode || (!isPaywallCampaignURL(payWallCode) ? payWallCode : "default"); | ||
const actionUrlFinal = | ||
!campaign || campaign === "default" ? actionUrl : `${actionUrl}?campaign=${campaign}`; | ||
|
||
return ( | ||
<SubscriptionOverlay displayMode={displayMode} usePortal={usePortal} className={className}> | ||
<SubscriptionDialog | ||
isLoggedIn={isLoggedIn} | ||
actionText={actionText} | ||
actionUrl={isPaywallCampaignURL(payWallCode) ? payWallCode : actionUrlFinal} | ||
headline={selectedOffer.pageTitle} | ||
linkPrompt={linkPrompt} | ||
linkText={linkText} | ||
linkUrl={linkUrl} | ||
reasonPrompt={reasonPrompt} | ||
subHeadline={selectedOffer.pageSubTitle} | ||
className={className} | ||
/> | ||
</SubscriptionOverlay> | ||
); | ||
}; | ||
|
||
export default PaywallOffer; |
122 changes: 122 additions & 0 deletions
122
blocks/subscriptions-block/components/PaywallOffer/index.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import React from "react"; | ||
import { render } from "@testing-library/react"; | ||
import isUrl from "is-url"; | ||
|
||
import { isServerSide } from "@wpmedia/arc-themes-components"; | ||
|
||
import PaywallOffer from "."; | ||
import usePaywall from "../usePaywall"; | ||
import useOffer from "../useOffer"; | ||
|
||
jest.mock("@wpmedia/arc-themes-components", () => ({ | ||
__esModule: true, | ||
isServerSide: jest.fn(() => false), | ||
})); | ||
|
||
jest.mock("is-url", () => ({ | ||
__esModule: true, | ||
default: jest.fn(() => false), | ||
})); | ||
|
||
jest.mock("../../components/useOffer"); | ||
jest.mock("../../../identity-block"); | ||
jest.mock("../../components/usePaywall"); | ||
|
||
useOffer.mockReturnValue({ | ||
offer: { | ||
pageTitle: "this the offer title", | ||
pageSubTitle: "this the offer subtitle", | ||
}, | ||
fetchOffer: () => ({ | ||
pageTitle: "this the offer title", | ||
pageSubTitle: "this the offer subtitle", | ||
}), | ||
}); | ||
|
||
usePaywall.mockReturnValue({ | ||
campaignCode: "default", | ||
isPaywalled: true, | ||
isRegisterwalled: false, | ||
}); | ||
|
||
/** | ||
* Below I pass usePortal to false that will in return | ||
* pass this down to the Subscription Overlay. | ||
* This boolean prevents ReactDOM.createPortal from being used. | ||
* Just checking with isServerSide doesn't work. Jest and Enzyme | ||
* still have poor support for ReactDOM.createPortal, so we need a way | ||
* to conditionally render ReactDOM.createPortal. | ||
*/ | ||
describe("The PaywallOffer component ", () => { | ||
it("returns null if serverSide", () => { | ||
isServerSide.mockReturnValue(true); | ||
render( | ||
<PaywallOffer | ||
actionText="Subscribe" | ||
actionUrl="/offer/" | ||
campaignCode="defaultish" | ||
linkPrompt="Already a subscriber?" | ||
linkText="Log In." | ||
linkUrl="/account/login" | ||
reasonPrompt="Subscribe to continue reading." | ||
usePortal={false} | ||
/> | ||
); | ||
expect(screen.html()).toBe(null); | ||
isServerSide.mockReset(); | ||
}); | ||
|
||
it("renders with correct markup", () => { | ||
render( | ||
<PaywallOffer | ||
actionText="Subscribe" | ||
actionUrl="/offer/" | ||
campaignCode="defaultish" | ||
linkPrompt="Already a subscriber?" | ||
linkText="Log In." | ||
linkUrl="/account/login" | ||
reasonPrompt="Subscribe to continue reading." | ||
usePortal={false} | ||
/> | ||
); | ||
|
||
expect(screen.getByText("Subscribe to continue reading.")).not.toBeNull(); | ||
expect(screen.getByText("Already a subscriber?")).not.toBeNull(); | ||
expect(screen.getByText("Log In.").closest("a")).toHaveAttribute("href", "/account/login"); | ||
|
||
expect(screen.getByText("this the offer title")).not.toBeNull(); | ||
expect(screen.getByText("this the offer subtitle")).not.toBeNull(); | ||
expect(screen.getByText("Subscribe")).not.toBeNull(); | ||
expect(screen.getByText("Subscribe").closest("a")).toHaveAttribute( | ||
"href", | ||
"/offer/?campaign=defaultish" | ||
); | ||
}); | ||
|
||
it("renders campaignCode if its a url", () => { | ||
isUrl.mockReturnValue(true); | ||
render( | ||
<PaywallOffer | ||
actionText="Subscribe" | ||
actionUrl="/offer/" | ||
campaignCode="./" | ||
usePortal={false} | ||
/> | ||
); | ||
expect(screen.getByText("Subscribe")).not.toBeNull(); | ||
expect(screen.getByText("Subscribe").closest("a")).toHaveAttribute( | ||
"href", | ||
"/offer/?campaign=./" | ||
); | ||
isUrl.mockReset(); | ||
}); | ||
|
||
it("renders without a query param if campaignCode is not passed", () => { | ||
isUrl.mockReturnValue(true); | ||
render(<PaywallOffer actionText="Subscribe" actionUrl="/offer/" usePortal={false} />); | ||
expect(screen.getByRole("button")).not.toBeNull(); | ||
expect(screen.getByText("Subscribe")).not.toBeNull(); | ||
expect(screen.getByRole("button").toHaveAttribute("href", "/offer/")); | ||
isUrl.mockReset(); | ||
}); | ||
}); |
41 changes: 41 additions & 0 deletions
41
blocks/subscriptions-block/components/RegwallOffer/index.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import React from "react"; | ||
|
||
import { isServerSide } from "@wpmedia/arc-themes-components"; | ||
import SubscriptionOverlay from "../SubscriptionOverlay"; | ||
import SubscriptionDialog from "../SubscriptionDialog"; | ||
|
||
const RegwallOffer = ({ | ||
actionText, | ||
actionUrl, | ||
displayMode, | ||
headlineText, | ||
linkPrompt, | ||
linkText, | ||
linkUrl, | ||
reasonPrompt, | ||
subheadlineText, | ||
usePortal = true, | ||
className, | ||
}) => { | ||
if (isServerSide()) { | ||
return null; | ||
} | ||
|
||
return ( | ||
<SubscriptionOverlay displayMode={displayMode} usePortal={usePortal} className={className}> | ||
<SubscriptionDialog | ||
actionText={actionText} | ||
actionUrl={actionUrl} | ||
headline={headlineText} | ||
linkPrompt={linkPrompt} | ||
linkText={linkText} | ||
linkUrl={linkUrl} | ||
reasonPrompt={reasonPrompt} | ||
subHeadline={subheadlineText} | ||
className={className} | ||
/> | ||
</SubscriptionOverlay> | ||
); | ||
}; | ||
|
||
export default RegwallOffer; |
69 changes: 69 additions & 0 deletions
69
blocks/subscriptions-block/components/RegwallOffer/index.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import React from "react"; | ||
import { render } from "@testing-library/react"; | ||
|
||
import { isServerSide } from "@wpmedia/arc-themes-components"; | ||
|
||
import RegwallOffer from "."; | ||
|
||
jest.mock("@wpmedia/arc-themes-components", () => ({ | ||
__esModule: true, | ||
isServerSide: jest.fn(() => false), | ||
})); | ||
|
||
jest.mock("is-url", () => ({ | ||
__esModule: true, | ||
default: jest.fn(() => false), | ||
})); | ||
|
||
/** | ||
* Below I pass usePortal to false that will in return | ||
* pass this down to the Subscription Overlay. | ||
* This boolean prevents ReactDOM.createPortal from being used. | ||
* Just checking with isServerSide doesn't work. Jest and Enzyme | ||
* still have poor support for ReactDOM.createPortal, so we need a way | ||
* to conditionally render ReactDOM.createPortal. | ||
*/ | ||
describe("The RegwallOffer component ", () => { | ||
it("returns null if serverSide", () => { | ||
isServerSide.mockReturnValue(true); | ||
render( | ||
<RegwallOffer | ||
actionText="Subscribe" | ||
actionUrl="/account/signup" | ||
headlineText="Headline" | ||
linkPrompt="Already a subscriber?" | ||
linkText="Log In." | ||
linkUrl="/account/login" | ||
reasonPrompt="Subscribe to continue reading." | ||
subheadlineText="Subheadline" | ||
usePortal={false} | ||
/> | ||
); | ||
expect(screen.html()).toBe(null); | ||
isServerSide.mockReset(); | ||
}); | ||
|
||
it("renders with correct markup", () => { | ||
render( | ||
<RegwallOffer | ||
actionText="Subscribe" | ||
actionUrl="/account/signup" | ||
headlineText="Headline" | ||
linkPrompt="Already a subscriber?" | ||
linkText="Log In." | ||
linkUrl="/account/login" | ||
reasonPrompt="Subscribe to continue reading." | ||
subheadlineText="Subheadline" | ||
usePortal={false} | ||
/> | ||
); | ||
|
||
expect(screen.getByText("Subscribe to continue reading.")).not.toBeNull(); | ||
expect(screen.getByText("Already a subscriber?")).not.toBeNull(); | ||
expect(screen.getByText("Log In.").closest("a")).toHaveAttribute("href", "/account/login"); | ||
|
||
expect(screen.getByText("Headline")).not.toBeNull(); | ||
expect(screen.getByText("Subheadline")).not.toBeNull(); | ||
expect(screen.getByText("Subscribe").closest("a")).toHaveAttribute("href", "/account/signup"); | ||
}); | ||
}); |
Oops, something went wrong.