Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-2210: Onboarding - Step 4 - Choose Plan #640

Merged
merged 32 commits into from Dec 10, 2020
Merged
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
81e9177
Create ghostery-search promo in plan view
benstrumeyer Nov 24, 2020
0a36f46
Add scroll arrow and cards
benstrumeyer Nov 24, 2020
6e38e7c
Add value prop list to cards
benstrumeyer Nov 25, 2020
890315d
Add radio button and hover effects to cards
benstrumeyer Nov 30, 2020
c475484
Add click handler to full card, not only radio button
benstrumeyer Nov 30, 2020
81bf436
Select default plan depending on current plan
benstrumeyer Nov 30, 2020
3609b8e
Add logic to display title and subtitle texts
benstrumeyer Nov 30, 2020
8a32f7b
Style radio button alt design
benstrumeyer Nov 30, 2020
cdcef84
Touch up radio button alt design
benstrumeyer Nov 30, 2020
40685f9
Make responsive
benstrumeyer Nov 30, 2020
2e299cc
Change CTA button to a button
benstrumeyer Dec 1, 2020
b452c0c
Add back backgrounds on small screens and add checkmarks for free card
benstrumeyer Dec 3, 2020
2ab5254
Add working links
benstrumeyer Dec 3, 2020
8dc513b
Refactor basic/plus/premium states
benstrumeyer Dec 5, 2020
ae09663
Make cards responsive with OR
benstrumeyer Dec 5, 2020
6154223
Add padding and change CTA button strings
benstrumeyer Dec 5, 2020
f61e456
Add hover effects to search and premium CTA buttons
benstrumeyer Dec 6, 2020
0865b2e
Conditionally show cards based on users subscriptions and refactor ar…
benstrumeyer Dec 6, 2020
dbe0507
Fix logic for showing caret
benstrumeyer Dec 6, 2020
957a436
Fix and refactor cta button links
benstrumeyer Dec 6, 2020
b56ca6e
Fix color of premium CTA button when displaying to a basic user
benstrumeyer Dec 6, 2020
cd0f937
Refactor and add comments
benstrumeyer Dec 6, 2020
11cbdbe
Remove unused css
benstrumeyer Dec 6, 2020
b1c585c
Update plus and premium checkout links to specify english
benstrumeyer Dec 6, 2020
3e37ddd
Merge branch 'ghostery-browser-intro-hub' into GH-2210-2
benstrumeyer Dec 6, 2020
9fccb04
Move RadioButton and RadioButtonGroup to shared components
benstrumeyer Dec 6, 2020
a6d647e
Rename Step4_ChoosePlanView to ChoosePlanView
benstrumeyer Dec 6, 2020
2bebdab
Use BASIC, PLUS, and PREMIUM constants from UpgradePlanViewConstants …
benstrumeyer Dec 6, 2020
e04c652
Update copywrite year
benstrumeyer Dec 6, 2020
cd70f46
Replace hex color codes with variables
benstrumeyer Dec 6, 2020
c9c5eed
Add border to unchecked radio button
benstrumeyer Dec 6, 2020
5775130
Merge branch 'ghostery-browser-intro-hub' into GH-2210-2
wlycdgr Dec 10, 2020
File filter
Filter file types
Jump to
Jump to file
Failed to load files.

Always

Just for now

Rename Step4_ChoosePlanView to ChoosePlanView

  • Loading branch information
benstrumeyer committed Dec 6, 2020
commit a6d647e50b8dc3229589a187a2f90f36b3c664c8
@@ -21,7 +21,7 @@ import WelcomeView from '../OnboardingViews/Step0_WelcomeView';
import LoginView from '../OnboardingViews/Step1_LoginView';
import BlockSettingsView from '../OnboardingViews/Step2_BlockSettingsView';
import ChooseDefaultSearchView from '../OnboardingViews/Step3_ChooseDefaultSearchView';
import ChoosePlanView from '../OnboardingViews/Step4_ChoosePlanView';
import ChoosePlanView from '../OnboardingViews/ChoosePlanView';
import SuccessView from '../OnboardingViews/Step5_SuccessView';

/**
@@ -1,5 +1,5 @@
/**
* Ghostery Browser Hub Choose Plan View Component
* Plan View Component
*
* Ghostery Browser Extension
* https://www.ghostery.com/
@@ -11,8 +11,367 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0
*/

import React from 'react';
import React, { Fragment } from 'react';
import ClassNames from 'classnames';
import PropTypes from 'prop-types';
import RadioButton from '../../../../shared-components/RadioButton';
import globals from '../../../../../src/classes/Globals';

const ChoosePlanView = () => <h1>Step 4: Choose Plan View</h1>;
const BASIC = 0;
const PLUS = 1;
const PREMIUM = 2;

const plusCheckoutLink = `${globals.CHECKOUT_BASE_URL}/en/plus`;
const premiumCheckoutLink = `${globals.CHECKOUT_BASE_URL}/en/premium`;

const searchPromo = () => (
<div className="ChoosePlanView__searchPromoContainer">
<div className="ChoosePlanView__searchLogo" />
<div className="ChoosePlanView__adFree">{ t('ghostery_browser_hub_onboarding_ad_free_with_ghostery_plus_subscription') }</div>
<div className="ChoosePlanView__adFreePromo">{ t('ghostery_browser_hub_onboarding_ad_free_promo') }</div>
<div className="ChoosePlanView__adFreePromoDescription">{ t('ghostery_browser_hub_onboarding_ad_free_promo_description') }</div>
</div>
);

const basicCard = (checked, handleClick) => {
const cardClassNames = ClassNames('ChoosePlanView__card basic', {
checked
});
return (
<div className="ChoosePlanView__cardOuter">
<div className={cardClassNames} onClick={handleClick} data-equalizer-watch>
<div className="ChoosePlanView__radioButtonContainer">
<RadioButton checked={checked} handleClick={handleClick} altDesign />
</div>
<div className="card-header-background-free" />
<div className="ghostery-free-image-container">
<div className="ghostery-free-image text-center" title="Ghostery Free" alt="Ghostery Free" />
</div>
<h2>Ghostery</h2>
<div className="ChoosePlanView__price">
<p className="ChoosePlanView__price-blue-alt font-size-36">{t('hub_upgrade_plan_free')}</p>
</div>
<p className="card-sub-header"><strong>{t('hub_upgrade_basic_protection')}</strong></p>
<div className="ChoosePlanView__valuePropList basic">
<div className="ChoosePlanView__cardSubCopy">
<span className="check blue" />
{t('ghostery_browser_hub_onboarding_private_search')}
</div>
<div className="ChoosePlanView__cardSubCopy">
<span className="check blue" />
{t('ghostery_browser_hub_onboarding_tracker_protection')}
</div>
<div className="ChoosePlanView__cardSubCopy">
<span className="check blue" />
{t('ghostery_browser_hub_onboarding_speedy_page_loads')}
</div>
<div className="ChoosePlanView__cardSubCopy">
<span className="check blue" />
{t('ghostery_browser_hub_onboarding_intelligence_technology')}
</div>
</div>
</div>
</div>
);
};

const plusCard = (checked, handleClick, showCTAButton = false) => {
const cardClassNames = ClassNames('ChoosePlanView__card plus', {
checked
});
return (
<Fragment>
<div className="ChoosePlanView__cardOuter">
<div className={cardClassNames} onClick={handleClick} data-equalizer-watch>
<div className="ChoosePlanView__radioButtonContainer">
<RadioButton checked={checked} handleClick={handleClick} altDesign />
</div>
<div className="ghostery-plus-image-container">
<div className="ghostery-plus-image" title="Ghostery Plus" alt="Ghostery Plus" />
</div>
<h2>Ghostery Plus</h2>
<div className="ChoosePlanView__price">
<Fragment>
<p className="ChoosePlanView__price-gold font-size-36">$4.99</p>
<p className="ChoosePlanView__price-gold sub-text font-size-12">{t('per_month')}</p>
</Fragment>
</div>
<p className="card-sub-header"><strong>{t('hub_upgrade_additional_protection')}</strong></p>
<div className="ChoosePlanView__valuePropList">
<div className="ChoosePlanView__cardSubCopy">
<span className="check blue" />
{t('ghostery_browser_hub_onboarding_private_search')}
</div>
<div className="ChoosePlanView__cardSubCopy">
<span className="check blue" />
{t('ghostery_browser_hub_onboarding_tracker_protection')}
</div>
<div className="ChoosePlanView__cardSubCopy">
<span className="check blue" />
{t('ghostery_browser_hub_onboarding_speedy_page_loads')}
</div>
<div className="ChoosePlanView__cardSubCopy">
<span className="check blue" />
{t('ghostery_browser_hub_onboarding_intelligence_technology')}
</div>
<div className="ChoosePlanView__cardSubCopy">
<span className="check blue" />
{t('ghostery_browser_hub_onboarding_ad_free')}
</div>
<div className="ChoosePlanView__cardSubCopy">
<span className="check blue" />
{t('ghostery_browser_hub_onboarding_supports_ghosterys_mission')}
</div>
</div>
</div>
</div>
{showCTAButton && (
// Route to next screen
<button className="ChoosePlanView__searchCTAButton" type="button">{t('ghostery_browser_hub_onboarding_keep')}</button>
)}
</Fragment>
);
};

const premiumCard = (checked, handleClick, showCTAButton = false) => {
const cardClassNames = ClassNames('ChoosePlanView__card premium', {
checked
});
return (
<Fragment>
<div className="ChoosePlanView__cardOuter">
<div className={cardClassNames} onClick={handleClick} data-equalizer-watch>
<div className="ChoosePlanView__radioButtonContainer">
<RadioButton checked={checked} handleClick={handleClick} altDesign />
</div>
<div className="ghostery-premium-image-container">
<div className="ghostery-premium-image card-image-top" title="Ghostery Premium" alt="Ghostery Premium" />
</div>
<div className="ghostery-premium-image-background" />
<h2>Ghostery Premium</h2>
<div className="ChoosePlanView__price">
<Fragment>
<p className="ChoosePlanView__price-purple sub-text font-size-36">$11.99</p>
<p className="ChoosePlanView__price-purple sub-text font-size-12">{t('per_month')}</p>
</Fragment>
</div>
<p className="card-sub-header"><strong>{t('hub_upgrade_maximum_protection')}</strong></p>
<div className="ChoosePlanView__valuePropList">
<div className="ChoosePlanView__cardSubCopy">
<span className="check blue" />
{t('ghostery_browser_hub_onboarding_private_search')}
</div>
<div className="ChoosePlanView__cardSubCopy">
<span className="check blue" />
{t('ghostery_browser_hub_onboarding_tracker_protection')}
</div>
<div className="ChoosePlanView__cardSubCopy">
<span className="check blue" />
{t('ghostery_browser_hub_onboarding_speedy_page_loads')}
</div>
<div className="ChoosePlanView__cardSubCopy">
<span className="check blue" />
{t('ghostery_browser_hub_onboarding_intelligence_technology')}
</div>
<div className="ChoosePlanView__cardSubCopy">
<span className="check blue" />
{t('ghostery_browser_hub_onboarding_ad_free')}
</div>
<div className="ChoosePlanView__cardSubCopy">
<span className="check blue" />
{t('ghostery_browser_hub_onboarding_supports_ghosterys_mission')}
</div>
<div className="ChoosePlanView__cardSubCopy">
<span className="check blue" />
VPN
</div>
<div className="ChoosePlanView__cardSubCopy">
<span className="check blue" />
{t('ghostery_browser_hub_onboarding_unlimited_bandwidth')}
</div>
</div>
</div>
</div>
{showCTAButton && (
<a className="ChoosePlanView__premiumCTAButton" href={premiumCheckoutLink} target="_blank" rel="noreferrer">{t('ghostery_browser_hub_onboarding_upgrade')}</a>
)}
</Fragment>
);
};

class ChoosePlanView extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedPlan: -1,
expanded: false
};
// User object doesn't get populated immediately, let's delay the first render
setTimeout(this.setDefaultPlan, 200);
}

setDefaultPlan = () => {
const { user } = this.props;
const isPlus = (user && user.plusAccess) || false;
const isPremium = (user && user.premiumAccess) || false;

if (isPremium) {
this.selectPremiumPlan();
return;
}
if (isPlus) {
this.selectPlusPlan();
return;
}
this.selectBasicPlan();
}

isBasicPlanChecked = () => {
const { selectedPlan } = this.state;
return (selectedPlan === BASIC);
};

isPlusPlanChecked = () => {
const { selectedPlan } = this.state;
return (selectedPlan === PLUS);
};

isPremiumPlanChecked = () => {
const { selectedPlan } = this.state;
return (selectedPlan === PREMIUM);
};

selectBasicPlan = () => this.setState({ selectedPlan: BASIC });

selectPlusPlan = () => this.setState({ selectedPlan: PLUS });

selectPremiumPlan = () => this.setState({ selectedPlan: PREMIUM });

toggleSection = () => {
const { expanded } = this.state;
if (expanded) {
this.setState({ expanded: !expanded });
} else {
this.setState({ expanded: !expanded });
}
};

renderTitleText = () => {
const { user } = this.props;
const isPlus = (user && user.plusAccess) || false;
const isPremium = (user && user.premiumAccess) || false;

if (isPremium) return t('ghostery_browser_hub_onboarding_already_premium_subscriber');
if (isPlus) return t('ghostery_browser_hub_onboarding_already_plus_subscriber');
return t('ghostery_browser_hub_onboarding_your_privacy_plan');
};

renderSubtitleText = (fromSearchSelectionScreen) => {
const { user } = this.props;
const isPlus = (user && user.plusAccess) || false;
const isPremium = (user && user.premiumAccess) || false;

if (fromSearchSelectionScreen) return t('ghostery_browser_hub_onboarding_based_on_your_privacy_preferences');
if (isPremium) return '';
if (isPlus) return t('ghostery_browser_hub_onboarding_keep_your_current_plan_or_upgrade');
return t('ghostery_browser_hub_onboarding_choose_an_option');
};

render() {
const { user, didNotSelectGhosterySearch } = this.props;
const { expanded, selectedPlan } = this.state;

const isBasic = !user;
const isPlus = (user && user.plusAccess && !user.premiumAccess) || false;
const isPremium = (user && user.premiumAccess) || false;

const arrowClassNames = ClassNames('ChoosePlanView__arrow', {
up: !expanded,
down: expanded
});

return (
<div className="ChoosePlanView">
<div className="ChoosePlanView__yourPrivacyPlan">{this.renderTitleText()}</div>
<div className="ChoosePlanView__subtitle">{this.renderSubtitleText(didNotSelectGhosterySearch)}</div>
{didNotSelectGhosterySearch && isBasic && (
<Fragment>
{searchPromo()}
{/* TODO: For the CTA button below,
1. If user is signed in, activate the user’s 7-day free trial for the Ghostery Search Plus plan
and move them to Step 5 if signed in
2. If user is signed out, clicking this should take them to Step 4b (linked)
*/}
<div className="ChoosePlanView__searchCTAButton">{t('ghostery_browser_hub_onboarding_start_trial')}</div>
<div className="ChoosePlanView__seeAllPlans" onClick={this.toggleSection}>{t('ghostery_browser_hub_onboarding_see_all_plans')}</div>
<div className={arrowClassNames} onClick={this.toggleSection} />
</Fragment>
)}
{((isBasic && !didNotSelectGhosterySearch) || expanded || isPlus || isPremium) && (
<div>
{(isPlus) ? (
<div className="ChoosePlanView__keepOrUpgradeContainer row align-center align-middle">
<div className="small-12 medium-12 large-4">
{plusCard(this.isPlusPlanChecked(), this.selectPlusPlan, isPlus)}
</div>
<div className="ChoosePlanView__or small-12 large-2">{t('ghostery_browser_hub_onboarding_or')}</div>
<div className="small-12 medium-12 large-4">
{premiumCard(this.isPremiumPlanChecked(), this.selectPremiumPlan, isPlus)}
</div>
</div>
) : (
<div className="ChoosePlanView__plansContainer row align-spaced">
{isBasic && (
basicCard(this.isBasicPlanChecked(), this.selectBasicPlan)
)}
{!isPremium && (
<Fragment>
{plusCard(this.isPlusPlanChecked(), this.selectPlusPlan)}
</Fragment>
)}
{premiumCard(this.isPremiumPlanChecked(), this.selectPremiumPlan)}
</div>
)}
{(isBasic && (
<div className="ChoosePlanView__ctaButtonContainer">
{(selectedPlan === BASIC) && (
// Change to route to next page
<button className="ChoosePlanView__searchCTAButton" type="button">{t('next')}</button>
)}
{selectedPlan === PLUS && (
<a className="ChoosePlanView__searchCTAButton" href={plusCheckoutLink} target="_blank" rel="noreferrer">{t('next')}</a>
)}
{selectedPlan === PREMIUM && (
<a className="ChoosePlanView__searchCTAButton" href={premiumCheckoutLink} target="_blank" rel="noreferrer">{t('next')}</a>
)}
</div>
))}
{isPremium && (
// Change to route to next page
<button className="ChoosePlanView__searchCTAButton" type="button">{t('next')}</button>
)}
</div>
)}
</div>
);
}
}

// PropTypes ensure we pass required props of the correct type
ChoosePlanView.propTypes = {
user: PropTypes.shape({
plusAccess: PropTypes.bool,
premiumAccess: PropTypes.bool,
}),
didNotSelectGhosterySearch: PropTypes.bool.isRequired,
};

// Default props used in the Plus View
ChoosePlanView.defaultProps = {
user: {
plusAccess: false,
premiumAccess: false,
},
};

export default ChoosePlanView;
ProTip! Use n and p to navigate between commits in a pull request.