Browse Source

chore(onboarding): Mobile experiment launch (#34208)

* ui changes

* CSS adjustments

* fix more styling

* enable experiment

* update template

* check isMobile before logging the experiment
Zhixing Zhang 2 years ago
parent
commit
e377f979b6

+ 3 - 1
src/sentry/templates/sentry/emails/onboarding-continuation.html

@@ -1,5 +1,7 @@
 {% extends "sentry/emails/base.html" %}
 {% load i18n %}
+{% load sentry_helpers %}
+
 {% block main %}
 <h3>Finish Onboarding</h3>
 
@@ -13,7 +15,7 @@
 </p>
 
 <p>
-  <a href="{{ onboarding_link }}" class="btn">Finish Onboarding</a>
+  <a href="{% absolute_uri onboarding_link %}" class="btn">Finish Onboarding</a>
 </p>
 
 {% endblock %}

+ 12 - 7
static/app/views/onboarding/onboardingController.tsx

@@ -1,3 +1,7 @@
+import {useEffect} from 'react';
+
+import {logExperiment} from 'sentry/utils/analytics';
+import isMobile from 'sentry/utils/isMobile';
 import withOrganization from 'sentry/utils/withOrganization';
 
 import TargetedOnboarding from './targetedOnboarding/onboarding';
@@ -5,13 +9,14 @@ import TargetedOnboarding from './targetedOnboarding/onboarding';
 type Props = Omit<React.ComponentPropsWithoutRef<typeof TargetedOnboarding>, 'projects'>;
 
 function OnboardingController({...rest}: Props) {
-  // TODO: uncomment
-  // useEffect(() => {
-  //   logExperiment({
-  //     key: 'TargetedOnboardingMobileRedirectExperiment',
-  //     organization: rest.organization,
-  //   });
-  // }, [rest.organization]);
+  useEffect(() => {
+    if (isMobile()) {
+      logExperiment({
+        key: 'TargetedOnboardingMobileRedirectExperiment',
+        organization: rest.organization,
+      });
+    }
+  }, [rest.organization]);
   return <TargetedOnboarding {...rest} />;
 }
 

+ 20 - 19
static/app/views/onboarding/targetedOnboarding/components/firstEventFooter.tsx

@@ -43,6 +43,15 @@ export default function FirstEventFooter({
   const client = useApi();
 
   const getSecondaryCta = () => {
+    // if hasn't sent first event, allow skiping.
+    // if last, no secondary cta
+    if (!hasFirstEvent && !isLast) {
+      return <Button onClick={onClickSetupLater}>{t('Next Platform')}</Button>;
+    }
+    return null;
+  };
+
+  const getPrimaryCta = ({firstIssue}: {firstIssue: null | true | Group}) => {
     if (
       isMobile() &&
       organization.experiments.TargetedOnboardingMobileRedirectExperiment === 'email-cta'
@@ -50,6 +59,7 @@ export default function FirstEventFooter({
       return (
         <Button
           to={`/onboarding/${organization.slug}/mobile-redirect/`}
+          priority="primary"
           onClick={() => {
             clientState &&
               client.requestPromise(
@@ -63,29 +73,20 @@ export default function FirstEventFooter({
               );
           }}
         >
-          {t('Do it Later')}
+          {t('Setup on Computer')}
         </Button>
       );
     }
-    // if hasn't sent first event, allow skiping.
-    // if last, no secondary cta
-    if (!hasFirstEvent && !isLast) {
-      return <Button onClick={onClickSetupLater}>{t('Next Platform')}</Button>;
-    }
-    return null;
-  };
-
-  const getPrimaryCta = ({firstIssue}: {firstIssue: null | true | Group}) => {
     // if hasn't sent first event, allow creation of sample error
     if (!hasFirstEvent) {
       return (
-        <StyledCreateSampleEventButton
+        <CreateSampleEventButton
           project={project}
           source="targted-onboarding"
           priority="primary"
         >
           {t('View Sample Error')}
-        </StyledCreateSampleEventButton>
+        </CreateSampleEventButton>
       );
     }
 
@@ -151,6 +152,7 @@ export default function FirstEventFooter({
 const OnboardingButtonBar = styled(ButtonBar)`
   margin: ${space(2)} ${space(4)};
   justify-self: end;
+  margin-left: auto;
 `;
 
 const AnimatedText = styled(motion.div, {
@@ -211,18 +213,17 @@ StatusWrapper.defaultProps = {
 const SkipOnboardingLink = styled(Link)`
   margin: auto ${space(4)};
   white-space: nowrap;
+  @media (max-width: ${p => p.theme.breakpoints[0]}) {
+    display: none;
+  }
 `;
 
 const GridFooter = styled(GenericFooter)`
   display: grid;
   grid-template-columns: 1fr 1fr 1fr;
   @media (max-width: ${p => p.theme.breakpoints[0]}) {
-    grid-template-columns: 1fr 1fr;
-  }
-`;
-
-const StyledCreateSampleEventButton = styled(CreateSampleEventButton)`
-  @media (max-width: ${p => p.theme.breakpoints[0]}) {
-    display: none;
+    display: flex;
+    flex-direction: row;
+    justify-content: end;
   }
 `;

+ 21 - 27
static/app/views/onboarding/targetedOnboarding/components/fullIntroduction.tsx

@@ -1,19 +1,23 @@
 import * as React from 'react';
 import {motion} from 'framer-motion';
 
-import {openInviteMembersModal} from 'sentry/actionCreators/modal';
-import Button from 'sentry/components/button';
 import {PlatformKey} from 'sentry/data/platformCategories';
 import platforms from 'sentry/data/platforms';
-import {t, tct} from 'sentry/locale';
+import {t} from 'sentry/locale';
+import {Organization} from 'sentry/types';
+import isMobile from 'sentry/utils/isMobile';
 
 import SetupIntroduction from './setupIntroduction';
 
 type Props = {
   currentPlatform: PlatformKey;
+  organization: Organization;
 };
 
-export default function FullIntroduction({currentPlatform}: Props) {
+export default function FullIntroduction({currentPlatform, organization}: Props) {
+  const showMobilePrompt =
+    isMobile() &&
+    organization.experiments.TargetedOnboardingMobileRedirectExperiment === 'email-cta';
   return (
     <React.Fragment>
       <SetupIntroduction
@@ -23,29 +27,19 @@ export default function FullIntroduction({currentPlatform}: Props) {
         )}
         platform={currentPlatform}
       />
-      <motion.p
-        variants={{
-          initial: {opacity: 0},
-          animate: {opacity: 1},
-          exit: {opacity: 0},
-        }}
-      >
-        {tct(
-          "Don't have a relationship with your terminal? [link:Invite your team instead].",
-          {
-            link: (
-              <Button
-                priority="link"
-                data-test-id="onboarding-getting-started-invite-members"
-                onClick={() => {
-                  openInviteMembersModal();
-                }}
-                aria-label={t('Invite your team instead')}
-              />
-            ),
-          }
-        )}
-      </motion.p>
+      {showMobilePrompt && (
+        <motion.p
+          variants={{
+            initial: {opacity: 0},
+            animate: {opacity: 1},
+            exit: {opacity: 0},
+          }}
+        >
+          {t(
+            'When you are ready, click Setup on Computer, and we will email you the installation instructions.'
+          )}
+        </motion.p>
+      )}
     </React.Fragment>
   );
 }

+ 0 - 1
static/app/views/onboarding/targetedOnboarding/onboarding.tsx

@@ -64,7 +64,6 @@ const MobileRedirectStep: StepDescriptor = {
   id: 'setup-docs',
   title: t('Install the Sentry SDK'),
   Component: MobileRedirect,
-  hasFooter: true,
   cornerVariant: 'top-left',
 };
 

+ 4 - 1
static/app/views/onboarding/targetedOnboarding/setupDocs.tsx

@@ -202,7 +202,10 @@ function SetupDocs({organization, projects, search}: Props) {
           {...{checkProjectHasFirstEvent, selectProject}}
         />
         <MainContent>
-          <FullIntroduction currentPlatform={currentPlatform} />
+          <FullIntroduction
+            currentPlatform={currentPlatform}
+            organization={organization}
+          />
           {getDynamicText({
             value: !hasError ? docs : loadingError,
             fixed: testOnlyAlert,

+ 0 - 3
static/less/layout.less

@@ -394,9 +394,6 @@ body.auth {
 
     // Hide fake sidebar on mobile
     background: none;
-
-    // Account for there being no footer on mobile
-    padding-bottom: 20px;
   }
 
   .app {