Browse Source

Merge pull request #8894 from overleaf/ab-cleanup-start-free-trial-button

[web] Cleanup StartFreeTrialButton component

GitOrigin-RevId: 67538882ff5a3389d2134ec6e4833431aa43f8e6
Alexandre Bourdin 4 years ago
parent
commit
4f340b0ed7

+ 14 - 15
services/web/frontend/js/features/pdf-preview/components/compile-time-warning.tsx

@@ -2,8 +2,8 @@ import { memo, useCallback, useEffect } from 'react'
 import { Button } from 'react-bootstrap'
 import { Trans, useTranslation } from 'react-i18next'
 import * as eventTracking from '../../../infrastructure/event-tracking'
+import StartFreeTrialButton from '../../../shared/components/start-free-trial-button'
 import { useDetachCompileContext } from '../../../shared/context/detach-compile-context'
-import { startFreeTrial } from '../../../main/account-upgrade'
 import usePersistedState from '../../../shared/hooks/use-persisted-state'
 
 const ONE_DAY = 24 * 60 * 60 * 24 * 1000
@@ -45,18 +45,13 @@ function CompileTimeWarning() {
     setDisplayStatus(displayStatus => ({ ...displayStatus, dismissed: true }))
   }, [getTimeSinceDisplayed, setShowCompileTimeWarning, setDisplayStatus])
 
-  const handleUpgradeClick = useCallback(
-    event => {
-      event.preventDefault()
-      startFreeTrial('compile-time-warning')
-      eventTracking.sendMB('compile-time-warning-upgrade-click', {
-        'time-since-displayed': getTimeSinceDisplayed(),
-      })
-      setShowCompileTimeWarning(false)
-      setDisplayStatus(displayStatus => ({ ...displayStatus, dismissed: true }))
-    },
-    [getTimeSinceDisplayed, setShowCompileTimeWarning, setDisplayStatus]
-  )
+  const handleUpgradeClick = useCallback(() => {
+    eventTracking.sendMB('compile-time-warning-upgrade-click', {
+      'time-since-displayed': getTimeSinceDisplayed(),
+    })
+    setShowCompileTimeWarning(false)
+    setDisplayStatus(displayStatus => ({ ...displayStatus, dismissed: true }))
+  }, [getTimeSinceDisplayed, setShowCompileTimeWarning, setDisplayStatus])
 
   if (!showCompileTimeWarning || displayStatus.dismissed) {
     return null
@@ -81,9 +76,13 @@ function CompileTimeWarning() {
           />
         </div>
         <div className="upgrade-prompt">
-          <Button bsStyle="primary" bsSize="sm" onClick={handleUpgradeClick}>
+          <StartFreeTrialButton
+            buttonProps={{ bsStyle: 'primary', bsSize: 'sm' }}
+            handleClick={handleUpgradeClick}
+            source="compile-time-warning"
+          >
             {t('upgrade')}
-          </Button>
+          </StartFreeTrialButton>
         </div>
       </div>
     </div>

+ 4 - 2
services/web/frontend/js/features/pdf-preview/components/timeout-upgrade-prompt.js

@@ -32,8 +32,10 @@ function TimeoutUpgradePrompt() {
             <p className="text-center">
               <StartFreeTrialButton
                 source="compile-timeout"
-                buttonStyle="success"
-                classes={{ button: 'row-spaced-small' }}
+                buttonProps={{
+                  bsStyle: 'success',
+                  className: 'row-spaced-small',
+                }}
               />
             </p>
           )}

+ 2 - 2
services/web/frontend/js/features/share-project-modal/components/add-collaborators-upgrade.js

@@ -33,8 +33,8 @@ export default function AddCollaboratorsUpgrade() {
       <p className="text-center row-spaced-thin">
         {user.allowedFreeTrial ? (
           <StartFreeTrialButton
-            buttonStyle="success"
-            setStartedFreeTrial={setStartedFreeTrial}
+            buttonProps={{ bsStyle: 'success' }}
+            handleClick={() => setStartedFreeTrial(true)}
             source="project-sharing"
           />
         ) : (

+ 2 - 2
services/web/frontend/js/features/share-project-modal/components/link-sharing.js

@@ -297,8 +297,8 @@ function LinkSharingUpgradePrompt({ canAddCollaborators }) {
           />
         </p>
         <StartFreeTrialButton
-          buttonStyle="success"
-          setStartedFreeTrial={setStartedFreeTrial}
+          buttonProps={{ bsStyle: 'success' }}
+          handleClick={() => setStartedFreeTrial(true)}
           source="link-sharing"
         />
       </Col>

+ 21 - 25
services/web/frontend/js/main/account-upgrade.js

@@ -8,36 +8,32 @@ const interstitialPaymentAfterPaywallVariant =
 function startFreeTrial(source, version, $scope) {
   const plan = 'collaborator_free_trial_7_days'
 
-  const w = window.open()
-  const go = function () {
-    if (typeof ga === 'function') {
-      ga('send', 'event', 'subscription-funnel', 'upgraded-free-trial', source)
-    }
-    eventTracking.sendMB('paywall-click', { 'paywall-type': source })
+  eventTracking.send('subscription-funnel', 'upgraded-free-trial', source)
+  eventTracking.sendMB('paywall-click', { 'paywall-type': source })
 
-    const searchParams = new URLSearchParams({ itm_campaign: source })
-
-    if (version) {
-      searchParams.set('itm_content', version)
-    }
-
-    let url
-    if (interstitialPaymentAfterPaywallVariant === 'active') {
-      url = `/user/subscription/choose-your-plan?${searchParams.toString()}`
-    } else {
-      searchParams.set('ssp', 'true')
-      searchParams.set('planCode', plan)
-      url = `/user/subscription/new?${searchParams.toString()}`
-    }
+  const searchParams = new URLSearchParams({
+    planCode: 'collaborator_free_trial_7_days',
+    ssp: 'true',
+    itm_campaign: source,
+  })
 
-    if ($scope) {
-      $scope.startedFreeTrial = true
-    }
+  if (version) {
+    searchParams.set('itm_content', version)
+  }
 
-    w.location = url
+  if ($scope) {
+    $scope.startedFreeTrial = true
   }
 
-  go()
+  if (interstitialPaymentAfterPaywallVariant === 'active') {
+    window.open(
+      `/user/subscription/choose-your-plan?${searchParams.toString()}`
+    )
+  } else {
+    searchParams.set('ssp', 'true')
+    searchParams.set('planCode', plan)
+    window.open(`/user/subscription/new?${searchParams.toString()}`)
+  }
 }
 
 function upgradePlan(source, $scope) {

+ 0 - 73
services/web/frontend/js/shared/components/start-free-trial-button.js

@@ -1,73 +0,0 @@
-import { useCallback, useEffect } from 'react'
-import { useTranslation } from 'react-i18next'
-import { Button } from 'react-bootstrap'
-import PropTypes from 'prop-types'
-import { useSplitTestContext } from '../context/split-test-context'
-import * as eventTracking from '../../infrastructure/event-tracking'
-
-export default function StartFreeTrialButton({
-  buttonStyle = 'info',
-  children,
-  classes = {},
-  setStartedFreeTrial,
-  source,
-}) {
-  const { t } = useTranslation()
-
-  const { splitTestVariants } = useSplitTestContext({
-    splitTestVariants: PropTypes.object,
-  })
-  const interstitialPaymentFromPaywallVariant =
-    splitTestVariants['interstitial-payment-from-paywall']
-
-  useEffect(() => {
-    eventTracking.sendMB('paywall-prompt', { 'paywall-type': source })
-  }, [source])
-
-  const handleClick = useCallback(
-    event => {
-      event.preventDefault()
-
-      eventTracking.send('subscription-funnel', 'upgraded-free-trial', source)
-      eventTracking.sendMB('paywall-click', { 'paywall-type': source })
-
-      if (setStartedFreeTrial) {
-        setStartedFreeTrial(true)
-      }
-
-      const params = new URLSearchParams({
-        planCode: 'collaborator_free_trial_7_days',
-        ssp: 'true',
-        itm_campaign: source,
-      })
-
-      if (interstitialPaymentFromPaywallVariant === 'active') {
-        window.open(
-          `/user/subscription/choose-your-plan?itm_campaign=${source}`
-        )
-      } else {
-        window.open(`/user/subscription/new?${params}`)
-      }
-    },
-    [setStartedFreeTrial, source, interstitialPaymentFromPaywallVariant]
-  )
-
-  return (
-    <Button
-      bsStyle={buttonStyle}
-      onClick={handleClick}
-      className={classes.button}
-    >
-      {children || t('start_free_trial')}
-    </Button>
-  )
-}
-StartFreeTrialButton.propTypes = {
-  buttonStyle: PropTypes.string,
-  children: PropTypes.any,
-  classes: PropTypes.shape({
-    button: PropTypes.string.isRequired,
-  }),
-  setStartedFreeTrial: PropTypes.func,
-  source: PropTypes.string.isRequired,
-}

+ 48 - 0
services/web/frontend/js/shared/components/start-free-trial-button.tsx

@@ -0,0 +1,48 @@
+import { MouseEventHandler, useCallback, useEffect } from 'react'
+import { useTranslation } from 'react-i18next'
+import { Button } from 'react-bootstrap'
+import { startFreeTrial } from '../../main/account-upgrade'
+import * as eventTracking from '../../infrastructure/event-tracking'
+
+type StartFreeTrialButtonProps = {
+  source: string
+  buttonProps?: Button.ButtonProps
+  children?: React.ReactNode
+  handleClick?: MouseEventHandler<Button>
+}
+
+export default function StartFreeTrialButton({
+  buttonProps = {
+    bsStyle: 'info',
+  },
+  children,
+  handleClick,
+  source,
+}: StartFreeTrialButtonProps) {
+  const { t } = useTranslation()
+
+  useEffect(() => {
+    eventTracking.sendMB('paywall-prompt', {
+      'paywall-type': source,
+    })
+  }, [source])
+
+  const onClick = useCallback(
+    event => {
+      event.preventDefault()
+
+      if (handleClick) {
+        handleClick(event)
+      }
+
+      startFreeTrial(source)
+    },
+    [handleClick, source]
+  )
+
+  return (
+    <Button {...buttonProps} onClick={onClick}>
+      {children || t('start_free_trial')}
+    </Button>
+  )
+}

+ 30 - 0
services/web/frontend/stories/start-free-trial-button.stories.js

@@ -0,0 +1,30 @@
+import StartFreeTrialButton from '../js/shared/components/start-free-trial-button'
+import { ScopeDecorator } from './decorators/scope'
+
+export const Default = args => {
+  return <StartFreeTrialButton {...args} />
+}
+
+export const CustomText = args => {
+  return (
+    <StartFreeTrialButton {...args}>Some Custom Text!</StartFreeTrialButton>
+  )
+}
+
+export const ButtonStyle = args => {
+  return (
+    <StartFreeTrialButton
+      {...args}
+      buttonProps={{ bsStyle: 'danger', bsSize: 'lg' }}
+    />
+  )
+}
+
+export default {
+  title: 'Shared / Components / Start Free Trial Button',
+  component: StartFreeTrialButton,
+  args: {
+    source: 'storybook',
+  },
+  decorators: [ScopeDecorator],
+}

+ 90 - 0
services/web/test/frontend/components/shared/start-free-trial-button.spec.tsx

@@ -0,0 +1,90 @@
+import StartFreeTrialButton from '../../../../frontend/js/shared/components/start-free-trial-button'
+
+describe('start free trial button', function () {
+  beforeEach(function () {
+    cy.intercept('POST', '/event/paywall-prompt', {
+      statusCode: 204,
+    }).as('event-paywall-prompt')
+    cy.intercept('POST', '/event/paywall-click', {
+      statusCode: 204,
+    }).as('event-paywall-click')
+  })
+
+  it('renders the button with default text', function () {
+    cy.mount(<StartFreeTrialButton source="cypress-test" />)
+
+    cy.wait('@event-paywall-prompt')
+      .its('request.body.paywall-type')
+      .should('eq', 'cypress-test')
+
+    cy.get('button').contains('Start Free Trial!')
+  })
+
+  it('renders the button with custom text', function () {
+    cy.mount(
+      <StartFreeTrialButton source="cypress-test">
+        Some Custom Text
+      </StartFreeTrialButton>
+    )
+
+    cy.wait('@event-paywall-prompt')
+      .its('request.body.paywall-type')
+      .should('eq', 'cypress-test')
+
+    cy.get('button').contains('Some Custom Text')
+  })
+
+  it('renders the button with styled button', function () {
+    cy.mount(
+      <StartFreeTrialButton
+        source="cypress-test"
+        buttonProps={{ bsStyle: 'danger', bsSize: 'lg' }}
+      />
+    )
+
+    cy.wait('@event-paywall-prompt')
+
+    cy.get('button.btn.btn-danger.btn-lg').contains('Start Free Trial!')
+  })
+
+  it('renders the button with custom class', function () {
+    cy.mount(
+      <StartFreeTrialButton
+        source="cypress-test"
+        buttonProps={{ className: 'ct-test-class' }}
+      />
+    )
+
+    cy.wait('@event-paywall-prompt')
+      .its('request.body.paywall-type')
+      .should('eq', 'cypress-test')
+
+    cy.get('.ct-test-class').contains('Start Free Trial!')
+  })
+
+  it('calls onClick callback and opens a new tab to the subscription page on click', function () {
+    const onClickStub = cy.stub()
+    cy.mount(
+      <StartFreeTrialButton source="cypress-test" handleClick={onClickStub} />
+    )
+
+    cy.wait('@event-paywall-prompt')
+
+    cy.window().then(win => {
+      cy.stub(win, 'open').as('Open')
+    })
+    cy.get('button.btn')
+      .contains('Start Free Trial!')
+      .click()
+      .then(() => {
+        cy.wait('@event-paywall-click')
+          .its('request.body.paywall-type')
+          .should('eq', 'cypress-test')
+        cy.get('@Open').should(
+          'have.been.calledOnceWithExactly',
+          '/user/subscription/new?planCode=collaborator_free_trial_7_days&ssp=true&itm_campaign=cypress-test'
+        )
+        expect(onClickStub).to.be.called
+      })
+  })
+})