Implement new three-way split test for the plans page (#11493)

* Update split test config and infrastructure for plans-page-layout-v3

* Implement view for `old-plans-page-annual` variant of the new split test:
    - Make `annual` the default view for all elements on the old plans page
    - Change the tooltip background to green for monthly/student view

* Implement a new design for the new plans page:
    - switch annual and monthly locations (annual on the left now)
    - change the tooltip background to green color for all choice
    - make the monthly-annual switch has green background if annual is chosen

* Fix mobile view plans page header style

GitOrigin-RevId: b2b3c6ac6adbe26bf6def7e072493f503793cfcb
This commit is contained in:
M Fahru 2023-01-27 08:04:33 -07:00 committed by Copybot
parent 6787e9c50d
commit 781c1c97c7
12 changed files with 139 additions and 62 deletions

View file

@ -56,16 +56,23 @@ async function plansPage(req, res) {
return defaultValue
}
const newPlansPageAssignmentV2 =
await SplitTestHandler.promises.getAssignment(
let plansPageLayoutV3Assignment = { variant: 'default' }
try {
plansPageLayoutV3Assignment = await SplitTestHandler.promises.getAssignment(
req,
res,
'plans-page-layout-v2-annual'
'plans-page-layout-v3'
)
} catch (error) {
logger.error(
{ err: error },
'failed to get "plans-page-layout-v3" split test assignment'
)
}
const newPlansPageVariantV2 =
newPlansPageAssignmentV2 &&
newPlansPageAssignmentV2.variant === 'new-plans-page'
const showNewPlansPage =
plansPageLayoutV3Assignment.variant === 'new-plans-page'
let defaultGroupPlanModalCurrency = 'USD'
if (validGroupPlanModalOptions.currency.includes(recommendedCurrency)) {
@ -73,14 +80,16 @@ async function plansPage(req, res) {
}
const groupPlanModalDefaults = {
plan_code: getDefault('plan', 'plan_code', 'collaborator'),
size: getDefault('number', 'size', newPlansPageVariantV2 ? '2' : '10'),
size: getDefault('number', 'size', showNewPlansPage ? '2' : '10'),
currency: getDefault('currency', 'currency', defaultGroupPlanModalCurrency),
usage: getDefault('usage', 'usage', 'enterprise'),
}
AnalyticsManager.recordEventForSession(req.session, 'plans-page-view')
AnalyticsManager.recordEventForSession(req.session, 'plans-page-view', {
'plans-page-layout-v3': plansPageLayoutV3Assignment.variant,
})
const template = newPlansPageVariantV2
const template = showNewPlansPage
? 'subscriptions/plans-marketing-v2'
: 'subscriptions/plans-marketing'
@ -96,7 +105,7 @@ async function plansPage(req, res) {
groupPlans: GroupPlansData,
groupPlanModalOptions,
groupPlanModalDefaults,
newPlansPageVariantV2,
plansPageLayoutV3Variant: plansPageLayoutV3Assignment.variant,
initialLocalizedGroupPrice:
SubscriptionHelper.generateInitialLocalizedGroupPrice(
recommendedCurrency

View file

@ -18,7 +18,7 @@ const config = {
},
eventTrackingKey: 'plans-page-click',
additionalEventSegmentation: {
'plans-page-layout-v2-annual': 'new-plans-page',
'plans-page-layout-v3': 'new-plans-page',
},
},
group: {
@ -36,7 +36,7 @@ const config = {
},
eventTrackingKey: 'plans-page-click',
additionalEventSegmentation: {
'plans-page-layout-v2-annual': 'new-plans-page',
'plans-page-layout-v3': 'new-plans-page',
},
},
student: {
@ -61,7 +61,7 @@ const config = {
},
eventTrackingKey: 'plans-page-click',
additionalEventSegmentation: {
'plans-page-layout-v2-annual': 'new-plans-page',
'plans-page-layout-v3': 'new-plans-page',
},
},
}

View file

@ -62,7 +62,10 @@ block content
h2 #{translate('compare_plan_features')}
.row
.col-md-6.col-md-offset-3
+plan_switch('table')
if (plansPageLayoutV3Variant === 'old-plans-page-annual')
+plan_switch_annual_default('table')
else
+plan_switch('table')
.col-md-3.text-right
+currency_dropdown
.row(event-tracking="features-table-viewed" event-tracking-ga="subscription-funnel" event-tracking-trigger="scroll" event-tracking-send-once="true" event-tracking-label="exp-")

View file

@ -61,7 +61,7 @@ div.modal.fade(tabindex="-1" role="dialog" data-ol-group-plan-modal)
event-tracking-mb="true"
event-tracking-trigger="click"
event-tracking-element="select"
event-segmentation='{"plans-page-layout-v2-annual": "' + (newPlansPageVariantV2 ? 'new-plans-page' : 'default') + '"}'
event-segmentation='{"plans-page-layout-v3": "' + (plansPageLayoutV3Variant) + '"}'
)
for size in groupPlanModalOptions.sizes
option(
@ -89,7 +89,7 @@ div.modal.fade(tabindex="-1" role="dialog" data-ol-group-plan-modal)
event-tracking-mb="true"
event-tracking-trigger="click"
event-tracking-element="checkbox"
event-segmentation='{"plans-page-layout-v2-annual": "' + (newPlansPageVariantV2 ? 'new-plans-page' : 'default') + '"}'
event-segmentation='{"plans-page-layout-v3": "' + (plansPageLayoutV3Variant) + '"}'
)
span This license is for educational purposes (applies to students or faculty using Overleaf for teaching)

View file

@ -217,11 +217,33 @@ mixin plan_switch(location)
li(data-ol-view-tab='student')
button.btn-default-outline #{translate("special_price_student")}
mixin plan_switch_annual_default(location)
ul.nav.nav-pills(class=(location === 'card' ? "above-cards" : ""))
li.active(data-ol-view-tab='annual' class=(location === 'card' ? "annual-saving-tooltip-container" : ""))
button.btn-default-outline #{translate("annual")}
if (location === 'card')
.tooltip.in.bottom.annual-saving-tooltip.annual-selected.green-background(
role="tooltip"
data-ol-annual-saving-tooltip
)
.tooltip-arrow
.tooltip-inner
span(data-ol-view="annual") #{translate("saving_20_percent")}
span(hidden data-ol-view="monthly") #{translate("save_20_percent_by_paying_annually")}
span(hidden data-ol-view="student") #{translate("save_20_percent_by_paying_annually")}
li(data-ol-view-tab='monthly')
button.btn-default-outline #{translate("monthly")}
li(data-ol-view-tab='student')
button.btn-default-outline #{translate("special_price_student")}
mixin allCardsAndControls(controlsRowSpaced, listLocation)
- var location = listLocation ? 'card_' + listLocation : 'card'
.row.top-switch(class=(controlsRowSpaced ? "row-spaced" : ""))
.col-md-6.col-md-offset-3
+plan_switch(location)
if (plansPageLayoutV3Variant === 'old-plans-page-annual')
+plan_switch_annual_default(location)
else
+plan_switch(location)
.col-md-2.text-right
+currency_dropdown

View file

@ -8,7 +8,7 @@ include ./_mixins
event-tracking="plans-page-toggle-plan"
event-tracking-mb="true"
event-tracking-trigger="click"
event-segmentation='{"button": "individual", "plans-page-layout-v2-annual": "new-plans-page"}'
event-segmentation='{"button": "individual", "plans-page-layout-v3": "new-plans-page"}'
)
button.btn.btn-default-outline #{translate("indvidual_plans")}
li.plans-v2-top-switch-group(
@ -16,7 +16,7 @@ include ./_mixins
event-tracking="plans-page-toggle-plan"
event-tracking-mb="true"
event-tracking-trigger="click"
event-segmentation='{"button": "group", "plans-page-layout-v2-annual": "new-plans-page"}'
event-segmentation='{"button": "group", "plans-page-layout-v3": "new-plans-page"}'
)
button.btn.btn-default-outline(
href="#"
@ -28,13 +28,13 @@ include ./_mixins
event-tracking="plans-page-toggle-plan"
event-tracking-mb="true"
event-tracking-trigger="click"
event-segmentation='{"button": "student", "plans-page-layout-v2-annual": "new-plans-page"}'
event-segmentation='{"button": "student", "plans-page-layout-v3": "new-plans-page"}'
)
button.btn.btn-default-outline(
href="#"
) #{translate("student_plans")}
+monthly_annual_switch("annual", "plans-page-toggle-period", '{"plans-page-layout-v2-annual": "new-plans-page"}')
+monthly_annual_switch("annual", "plans-page-toggle-period", '{"plans-page-layout-v3": "new-plans-page"}')
.row(hidden data-ol-plans-v2-license-picker-container)
.col-sm-12

View file

@ -344,7 +344,7 @@ mixin group_plans_license_picker()
event-tracking-mb="true"
event-tracking-trigger="click"
event-tracking-element="select"
event-segmentation='{"plans-page-layout-v2-annual": "new-plans-page"}'
event-segmentation='{"plans-page-layout-v3": "new-plans-page"}'
)
option(value="2") 2
option(value="3") 3
@ -364,7 +364,7 @@ mixin group_plans_license_picker()
event-tracking-mb="true"
event-tracking-trigger="click"
event-tracking-element="checkbox"
event-segmentation='{"plans-page-layout-v2-annual": "new-plans-page"}'
event-segmentation='{"plans-page-layout-v3": "new-plans-page"}'
)
span #{translate("apply_educational_discount")}
//- will only appear on screen width >= 768px (using CSS)
@ -573,10 +573,21 @@ mixin table_sticky_header_all(plansV2Config)
+plans_v2_table_sticky_header(true, plansV2Config.student)
mixin monthly_annual_switch(initialState, eventTracking, eventSegmentation)
- var monthlyAnnualToggleChecked = initialState === 'annual'
- var monthlyAnnualToggleChecked = initialState === 'monthly'
.row
.col-md-4.col-md-offset-4.text-centered.plans-v2-m-a-switch-container(data-ol-plans-v2-m-a-switch-container)
span.underline(data-ol-plans-v2-m-a-switch-text='monthly') #{translate("monthly")}
.plans-v2-m-a-switch-annual-text-container
span.underline(data-ol-plans-v2-m-a-switch-text='annual') #{translate("annual")}
.tooltip.in.left.plans-v2-m-a-tooltip(
role="tooltip"
data-ol-plans-v2-m-a-tooltip
class=monthlyAnnualToggleChecked ? 'plans-v2-m-a-tooltip-monthly-selected' : ''
)
.tooltip-arrow
.tooltip-inner
span(hidden=!monthlyAnnualToggleChecked data-ol-tooltip-period='monthly') #{translate("save_20_percent_by_paying_annually")}
span(hidden=monthlyAnnualToggleChecked data-ol-tooltip-period='annual') #{translate("saving_20_percent")}
label.plans-v2-m-a-switch(data-ol-plans-v2-m-a-switch)
input(
type="checkbox"
@ -590,14 +601,4 @@ mixin monthly_annual_switch(initialState, eventTracking, eventSegmentation)
event-segmentation=eventSegmentation
)
span
.plans-v2-m-a-switch-annual-text-container
span(data-ol-plans-v2-m-a-switch-text='annual') #{translate("annual")}
.tooltip.in.right.plans-v2-m-a-tooltip(
role="tooltip"
data-ol-plans-v2-m-a-tooltip
class=monthlyAnnualToggleChecked ? 'plans-v2-m-a-tooltip-annual-selected' : ''
)
.tooltip-arrow
.tooltip-inner
span(hidden=monthlyAnnualToggleChecked data-ol-tooltip-period='monthly') #{translate("save_20_percent_by_paying_annually")}
span(hidden=!monthlyAnnualToggleChecked data-ol-tooltip-period='annual') #{translate("saving_20_percent")}
span(data-ol-plans-v2-m-a-switch-text='monthly') #{translate("monthly")}

View file

@ -12,7 +12,7 @@ export function toggleMonthlyAnnualSwitching(
containerEl.classList.toggle('disabled', view === 'group')
checkbox.disabled = view === 'group'
checkbox.checked = currentMonthlyAnnualSwitchValue === 'annual'
checkbox.checked = currentMonthlyAnnualSwitchValue === 'monthly'
switchMonthlyAnnual(currentMonthlyAnnualSwitchValue)
}
@ -20,8 +20,8 @@ export function toggleMonthlyAnnualSwitching(
export function switchMonthlyAnnual(currentMonthlyAnnualSwitchValue) {
const el = document.querySelector('[data-ol-plans-v2-m-a-tooltip]')
el.classList.toggle(
'plans-v2-m-a-tooltip-annual-selected',
currentMonthlyAnnualSwitchValue === 'annual'
'plans-v2-m-a-tooltip-monthly-selected',
currentMonthlyAnnualSwitchValue === 'monthly'
)
document.querySelectorAll('[data-ol-tooltip-period]').forEach(el => {
@ -51,7 +51,7 @@ function changeMonthlyAnnualTooltipPosition() {
const el = document.querySelector('[data-ol-plans-v2-m-a-tooltip]')
el.classList.toggle('bottom', smallScreen)
el.classList.toggle('right', !smallScreen)
el.classList.toggle('left', !smallScreen)
}
// click event listener for monthly-annual switch

View file

@ -19,7 +19,7 @@ import { updateLinkTargets } from '../plans'
// There's some difference between the monthly and annual UI
// and since monthly-annual switch is disabled for the group tab,
// we need to introduce a new variable to store the information
let currentMonthlyAnnualSwitchValue = 'monthly'
let currentMonthlyAnnualSwitchValue = 'annual'
function selectTab(viewTab) {
document.querySelectorAll('[data-ol-plans-v2-view-tab]').forEach(el => {
@ -105,6 +105,7 @@ function selectViewFromHash() {
if (view) {
// make sure the selected view is valid
if (document.querySelector(`[data-ol-plans-v2-view-tab="${view}"]`)) {
// set annual as the default
currentMonthlyAnnualSwitchValue = 'annual'
selectTab(view)
// clear the hash so it doesn't persist when switching plans
@ -119,14 +120,14 @@ function selectViewFromHash() {
document
.querySelector('[data-ol-plans-v2-m-a-switch]')
.addEventListener('click', () => {
const isAnnualPricing = document.querySelector(
const isMonthlyPricing = document.querySelector(
'[data-ol-plans-v2-m-a-switch] input[type="checkbox"]'
).checked
if (isAnnualPricing) {
currentMonthlyAnnualSwitchValue = 'annual'
} else {
if (isMonthlyPricing) {
currentMonthlyAnnualSwitchValue = 'monthly'
} else {
currentMonthlyAnnualSwitchValue = 'annual'
}
switchMonthlyAnnual(currentMonthlyAnnualSwitchValue)

View file

@ -3,8 +3,6 @@ import '../../../features/plans/group-plan-modal'
import * as eventTracking from '../../../infrastructure/event-tracking'
import getMeta from '../../../utils/meta'
const PLANS_PAGE_LAYOUT_V2_ANNUAL = 'plans-page-layout-v2-annual'
let currentView = 'monthly'
let currentCurrencyCode = getMeta('ol-recommendedCurrency')
@ -16,24 +14,28 @@ function selectView(view) {
el.classList.remove('active')
}
})
document.querySelectorAll('[data-ol-view]').forEach(el => {
el.hidden = el.getAttribute('data-ol-view') !== view
})
updateAnnualSavingBanner(view)
currentView = view
updateLinkTargets()
}
function setUpViewSwitching(liEl) {
const plansPageV2SplitTestVariant =
getMeta('ol-splitTestVariants')?.[PLANS_PAGE_LAYOUT_V2_ANNUAL] ?? 'default'
const plansPageLayoutV3Variant =
getMeta('ol-splitTestVariants')?.['plans-page-layout-v3'] ?? 'default'
const view = liEl.getAttribute('data-ol-view-tab')
liEl.querySelector('button').addEventListener('click', function (e) {
e.preventDefault()
eventTracking.send('subscription-funnel', 'plans-page', `${view}-prices`)
eventTracking.sendMB('plans-page-toggle', {
button: view,
PLANS_PAGE_LAYOUT_V2_ANNUAL: plansPageV2SplitTestVariant,
'plans-page-layout-v3': plansPageLayoutV3Variant,
})
selectView(view)
})
@ -41,6 +43,7 @@ function setUpViewSwitching(liEl) {
function setUpCurrencySwitching(linkEl) {
const currencyCode = linkEl.getAttribute('data-ol-currencyCode-switch')
linkEl.addEventListener('click', function (e) {
e.preventDefault()
document.querySelectorAll('[data-ol-currencyCode]').forEach(el => {
@ -53,18 +56,22 @@ function setUpCurrencySwitching(linkEl) {
}
function setUpSubscriptionTracking(linkEl) {
const plansPageV2SplitTestVariant =
getMeta('ol-splitTestVariants')?.[PLANS_PAGE_LAYOUT_V2_ANNUAL] ?? 'default'
const plansPageLayoutV3Variant =
getMeta('ol-splitTestVariants')?.['plans-page-layout-v3'] ?? 'default'
const plan =
linkEl.getAttribute('data-ol-tracking-plan') ||
linkEl.getAttribute('data-ol-start-new-subscription')
const location = linkEl.getAttribute('data-ol-location')
const period = linkEl.getAttribute('data-ol-item-view') || currentView
const DEFAULT_EVENT_TRACKING_KEY = 'plans-page-click'
const eventTrackingKey =
linkEl.getAttribute('data-ol-event-tracking-key') ||
DEFAULT_EVENT_TRACKING_KEY
const eventTrackingSegmentation = {
button: plan,
location,
@ -72,8 +79,7 @@ function setUpSubscriptionTracking(linkEl) {
}
if (eventTrackingKey === DEFAULT_EVENT_TRACKING_KEY) {
eventTrackingSegmentation[PLANS_PAGE_LAYOUT_V2_ANNUAL] =
plansPageV2SplitTestVariant
eventTrackingSegmentation['plans-page-layout-v3'] = plansPageLayoutV3Variant
}
linkEl.addEventListener('click', function () {
@ -131,6 +137,23 @@ function updateAnnualSavingBanner(view) {
}
}
function makeAnnualViewAsDefault() {
const plansPageLayoutV3Variant =
getMeta('ol-splitTestVariants')?.['plans-page-layout-v3'] ?? 'default'
// there are a handful of html elements that will switch between monthly and annual view
// with the `hidden` attribute.
// On the variant `old-plans-page-annual`, we want annual as the default.
// So, instead of changing the pugfiles directly, we change the default with this
if (plansPageLayoutV3Variant === 'old-plans-page-annual') {
document.querySelectorAll('[data-ol-view]').forEach(el => {
const view = el.getAttribute('data-ol-view')
el.hidden = view !== 'annual'
})
}
}
function selectViewFromHash() {
try {
const params = new URLSearchParams(window.location.hash.substring(1))
@ -159,3 +182,6 @@ updateLinkTargets()
selectViewFromHash()
window.addEventListener('hashchange', selectViewFromHash)
// only for `old-plans-page-annual` variant of the `plans-page-layout-v3` split test
makeAnnualViewAsDefault()

View file

@ -118,6 +118,7 @@
white-space: unset;
padding: 4px 8px;
font-size: 14px;
width: 100%;
}
}
}
@ -183,6 +184,10 @@ button.plans-v2-btn-header {
}
input + span {
background-color: @ol-green;
}
input:checked + span {
background-color: @ol-blue-gray-4;
}
@ -225,17 +230,17 @@ button.plans-v2-btn-header {
width: auto;
padding: @padding-xs @padding-sm;
border-radius: 4px;
left: 100%;
right: 110%;
top: 0;
z-index: 0;
&.plans-v2-m-a-tooltip-annual-selected {
&.tooltip.in.left {
.tooltip-inner {
background-color: @ol-green;
}
.tooltip-arrow {
border-right-color: @ol-green;
border-left-color: @ol-green;
}
}
@ -244,22 +249,24 @@ button.plans-v2-btn-header {
}
@media (max-width: @screen-xs-max) {
left: -121%;
right: -31%;
top: unset;
&.plans-v2-m-a-tooltip-annual-selected {
left: -33%;
&.tooltip.in.bottom {
.tooltip-inner {
background-color: @ol-green;
}
.tooltip-arrow {
border-right-color: unset;
border-left-color: unset;
border-bottom-color: @ol-green;
}
}
&.plans-v2-m-a-tooltip-monthly-selected {
right: -119%;
}
span {
font-size: @font-size-extra-small;
position: relative;

View file

@ -155,6 +155,14 @@
border-bottom-color: @ol-green;
}
}
&.green-background {
.tooltip-inner {
background-color: @ol-green;
}
.tooltip-arrow {
border-bottom-color: @ol-green;
}
}
.tooltip-inner {
max-width: none;
}