mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-04-15 03:01:37 -05:00
Added recommendation modal trigger on signup (#17925)
refs https://github.com/TryGhost/Product/issues/3771 - if recommendations are enabled, render the recommendation modal on sign up, in Portal - for free signups, the recommendations modal is rendered after clicking on the magic link - for paid signups, the recommendations modal is rendered after Stripe Checkout - the recommendations modal is not rendered on a free to paid upgrade
This commit is contained in:
parent
d4b717493c
commit
d7504bdbf5
5 changed files with 150 additions and 9 deletions
|
@ -96,15 +96,25 @@ async function signin({data, api, state}) {
|
|||
|
||||
async function signup({data, state, api}) {
|
||||
try {
|
||||
const {recommendations_enabled: recommendationsEnabled = false} = state.site;
|
||||
const {recommendations = []} = state.site;
|
||||
let successUrl = undefined;
|
||||
|
||||
if (recommendationsEnabled && recommendations.length > 0) {
|
||||
const currentUrl = window.location.origin + window.location.pathname;
|
||||
successUrl = `${currentUrl}#/portal/recommendations`;
|
||||
}
|
||||
|
||||
let {plan, tierId, cadence, email, name, newsletters, offerId} = data;
|
||||
|
||||
if (plan.toLowerCase() === 'free') {
|
||||
await api.member.sendMagicLink({emailType: 'signup', ...data});
|
||||
await api.member.sendMagicLink({emailType: 'signup', ...data, redirect: successUrl});
|
||||
} else {
|
||||
if (tierId && cadence) {
|
||||
await api.member.checkoutPlan({plan, tierId, cadence, email, name, newsletters, offerId});
|
||||
await api.member.checkoutPlan({plan, tierId, cadence, email, name, newsletters, offerId, successUrl});
|
||||
} else {
|
||||
({tierId, cadence} = getProductCadenceFromPrice({site: state?.site, priceId: plan}));
|
||||
await api.member.checkoutPlan({plan, tierId, cadence, email, name, newsletters, offerId});
|
||||
await api.member.checkoutPlan({plan, tierId, cadence, email, name, newsletters, offerId, successUrl});
|
||||
}
|
||||
return {
|
||||
page: 'loading'
|
||||
|
|
|
@ -537,9 +537,7 @@ describe('Signup', () => {
|
|||
window.location.hash = '';
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Signup', () => {
|
||||
describe('as free member on multi tier site', () => {
|
||||
test('with default settings', async () => {
|
||||
const {
|
||||
|
@ -784,4 +782,114 @@ describe('Signup', () => {
|
|||
window.location.hash = '';
|
||||
});
|
||||
});
|
||||
|
||||
describe('recommendations', () => {
|
||||
describe('free member signup', () => {
|
||||
test('does not redirect to the recommendations modal if recommendations are disabled', async () => {
|
||||
const {
|
||||
ghostApi, emailInput, nameInput, chooseBtns
|
||||
} = await setup({
|
||||
// Recommendations are disabled (default)
|
||||
site: FixtureSite.singleTier.basic
|
||||
});
|
||||
|
||||
// Fill signup form as free member
|
||||
fireEvent.change(nameInput, {target: {value: 'Jamie Larsen'}});
|
||||
fireEvent.change(emailInput, {target: {value: 'jamie@example.com'}});
|
||||
fireEvent.click(chooseBtns[0]);
|
||||
|
||||
expect(ghostApi.member.sendMagicLink).toHaveBeenLastCalledWith({
|
||||
email: 'jamie@example.com',
|
||||
emailType: 'signup',
|
||||
name: 'Jamie Larsen',
|
||||
plan: 'free',
|
||||
redirect: undefined
|
||||
});
|
||||
});
|
||||
|
||||
test('redirects the recommendations modal if recommendations are enabled', async () => {
|
||||
const {
|
||||
ghostApi, emailInput, nameInput, chooseBtns
|
||||
} = await setup({
|
||||
// Recommendations are enabled
|
||||
site: FixtureSite.singleTier.withRecommendations
|
||||
});
|
||||
|
||||
// Fill signup form as free member
|
||||
fireEvent.change(nameInput, {target: {value: 'Jamie Larsen'}});
|
||||
fireEvent.change(emailInput, {target: {value: 'jamie@example.com'}});
|
||||
fireEvent.click(chooseBtns[0]);
|
||||
|
||||
const currentUrl = new URL(window.location.origin + window.location.pathname);
|
||||
|
||||
expect(ghostApi.member.sendMagicLink).toHaveBeenLastCalledWith({
|
||||
email: 'jamie@example.com',
|
||||
emailType: 'signup',
|
||||
name: 'Jamie Larsen',
|
||||
plan: 'free',
|
||||
redirect: `${currentUrl}#/portal/recommendations`
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('paid member signup', () => {
|
||||
test('does not redirect to the recommendations modal if recommendations are disabled', async () => {
|
||||
const {
|
||||
ghostApi, emailInput, nameInput, popupIframeDocument, submitButton
|
||||
} = await setup({
|
||||
// Recommendations are disabled (default)
|
||||
site: FixtureSite.singleTier.basic
|
||||
});
|
||||
|
||||
const yearlyPlanContainer = within(popupIframeDocument).queryByText(/Yearly$/);
|
||||
const singleTierProduct = FixtureSite.singleTier.basic.products.find(p => p.type === 'paid');
|
||||
|
||||
// Fill signup form as free member
|
||||
fireEvent.change(nameInput, {target: {value: 'Jamie Larsen'}});
|
||||
fireEvent.change(emailInput, {target: {value: 'jamie@example.com'}});
|
||||
fireEvent.click(yearlyPlanContainer.parentNode);
|
||||
fireEvent.click(submitButton);
|
||||
|
||||
expect(ghostApi.member.checkoutPlan).toHaveBeenLastCalledWith({
|
||||
email: 'jamie@example.com',
|
||||
name: 'Jamie Larsen',
|
||||
offerId: undefined,
|
||||
plan: singleTierProduct.yearlyPrice.id,
|
||||
tierId: singleTierProduct.id,
|
||||
cadence: 'year',
|
||||
successUrl: undefined
|
||||
});
|
||||
});
|
||||
|
||||
test('redirects to the recommendations modal if recommendations are enabled', async () => {
|
||||
const {
|
||||
ghostApi, emailInput, nameInput, popupIframeDocument, submitButton
|
||||
} = await setup({
|
||||
// Recommendations are disabled (default)
|
||||
site: FixtureSite.singleTier.withRecommendations
|
||||
});
|
||||
|
||||
const yearlyPlanContainer = within(popupIframeDocument).queryByText(/Yearly$/);
|
||||
const singleTierProduct = FixtureSite.singleTier.basic.products.find(p => p.type === 'paid');
|
||||
|
||||
// Fill signup form as free member
|
||||
fireEvent.change(nameInput, {target: {value: 'Jamie Larsen'}});
|
||||
fireEvent.change(emailInput, {target: {value: 'jamie@example.com'}});
|
||||
fireEvent.click(yearlyPlanContainer.parentNode);
|
||||
fireEvent.click(submitButton);
|
||||
|
||||
const currentUrl = new URL(window.location.origin + window.location.pathname);
|
||||
|
||||
expect(ghostApi.member.checkoutPlan).toHaveBeenLastCalledWith({
|
||||
email: 'jamie@example.com',
|
||||
name: 'Jamie Larsen',
|
||||
offerId: undefined,
|
||||
plan: singleTierProduct.yearlyPrice.id,
|
||||
tierId: singleTierProduct.id,
|
||||
cadence: 'year',
|
||||
successUrl: `${currentUrl}#/portal/recommendations`
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -40,7 +40,9 @@ export function getSiteData({
|
|||
portalButtonStyle: portal_button_style = 'icon-and-text',
|
||||
membersSupportAddress: members_support_address = 'support@example.com',
|
||||
newsletters = [],
|
||||
commentsEnabled
|
||||
commentsEnabled,
|
||||
recommendations = [],
|
||||
recommendationsEnabled
|
||||
} = {}) {
|
||||
return {
|
||||
title,
|
||||
|
@ -65,7 +67,9 @@ export function getSiteData({
|
|||
portal_button_style,
|
||||
members_support_address,
|
||||
comments_enabled: !!commentsEnabled,
|
||||
newsletters
|
||||
newsletters,
|
||||
recommendations,
|
||||
recommendations_enabled: !!recommendationsEnabled
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -102,7 +102,9 @@ const baseSingleTierSite = getSiteData({
|
|||
portalButtonIcon: 'icon-1',
|
||||
portalButtonSignupText: 'Subscribe now',
|
||||
portalButtonStyle: 'icon-and-text',
|
||||
membersSupportAddress: 'support@example.com'
|
||||
membersSupportAddress: 'support@example.com',
|
||||
recommendationsEnabled: false,
|
||||
recommendations: []
|
||||
});
|
||||
|
||||
const baseMultiTierSite = getSiteData({
|
||||
|
@ -130,7 +132,9 @@ const baseMultiTierSite = getSiteData({
|
|||
portalButtonIcon: 'icon-1',
|
||||
portalButtonSignupText: 'Subscribe now',
|
||||
portalButtonStyle: 'icon-and-text',
|
||||
membersSupportAddress: 'support@example.com'
|
||||
membersSupportAddress: 'support@example.com',
|
||||
recommendationsEnabled: false,
|
||||
recommendations: []
|
||||
});
|
||||
|
||||
export const site = {
|
||||
|
@ -168,6 +172,11 @@ export const site = {
|
|||
membersDisabled: {
|
||||
...baseSingleTierSite,
|
||||
members_signup_access: 'none'
|
||||
},
|
||||
withRecommendations: {
|
||||
...baseSingleTierSite,
|
||||
recommendations_enabled: true,
|
||||
recommendations: [{title: 'Recommendation 1', url: 'https://recommendation-1.org'}, {title: 'Recommendation 2', url: 'https://recommendation-2.org'}]
|
||||
}
|
||||
},
|
||||
multipleTiers: {
|
||||
|
@ -183,6 +192,11 @@ export const site = {
|
|||
withoutName: {
|
||||
...baseMultiTierSite,
|
||||
portal_name: false
|
||||
},
|
||||
withRecommendations: {
|
||||
...baseMultiTierSite,
|
||||
recommendations_enabled: true,
|
||||
recommendations: [{title: 'Recommendation 1', url: 'https://recommendation-1.org'}, {title: 'Recommendation 2', url: 'https://recommendation-2.org'}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -105,6 +105,11 @@ export default Model.extend(ValidationEngine, {
|
|||
donationsCurrency: attr('string'),
|
||||
donationsSuggestedAmount: attr('number'),
|
||||
|
||||
/**
|
||||
* Recommendations
|
||||
*/
|
||||
recommendationsEnabled: attr('boolean'),
|
||||
|
||||
// HACK - not a real model attribute but a workaround for Ember Data not
|
||||
// exposing meta from save responses
|
||||
_meta: attr()
|
||||
|
|
Loading…
Add table
Reference in a new issue