Browse Source

feat(feedback): add customization toggles for onboarding (#66107)

Adds two customization toggles for feedback onboarding: `isNameRequired`
and `isEmailRequired` (the toggles are on by default)

<img width="454" alt="SCR-20240229-muxk"
src="https://github.com/getsentry/sentry/assets/56095982/c868196b-1dd0-4f2b-a997-289d8473ee41">

The toggles don't show up for platforms that don't have the widget
instructions.


https://github.com/getsentry/sentry/assets/56095982/15126ce0-696e-4df4-ae1e-dadd2bdaf921
Michelle Zhang 1 year ago
parent
commit
e8b712feff

+ 45 - 0
static/app/components/feedback/feedbackOnboarding/feedbackConfigToggle.tsx

@@ -0,0 +1,45 @@
+import styled from '@emotion/styled';
+
+import Switch from 'sentry/components/switchButton';
+import {t} from 'sentry/locale';
+import {space} from 'sentry/styles/space';
+
+function FeedbackConfigToggle({
+  emailToggle,
+  onEmailToggle,
+  nameToggle,
+  onNameToggle,
+}: {
+  emailToggle: boolean;
+  nameToggle: boolean;
+  onEmailToggle: () => void;
+  onNameToggle: () => void;
+}) {
+  return (
+    <SwitchWrapper>
+      <SwitchItem htmlFor="name">
+        {t('Name Required')}
+        <Switch id="name" toggle={onNameToggle} size="lg" isActive={nameToggle} />
+      </SwitchItem>
+      <SwitchItem htmlFor="email">
+        {t('Email Required')}
+        <Switch id="email" toggle={onEmailToggle} size="lg" isActive={emailToggle} />
+      </SwitchItem>
+    </SwitchWrapper>
+  );
+}
+
+const SwitchItem = styled('label')`
+  display: flex;
+  align-items: center;
+  gap: ${space(1)};
+`;
+
+const SwitchWrapper = styled('div')`
+  display: flex;
+  align-items: center;
+  gap: ${space(2)};
+  padding-top: ${space(0.5)};
+`;
+
+export default FeedbackConfigToggle;

+ 33 - 5
static/app/components/feedback/feedbackOnboarding/feedbackOnboardingLayout.tsx

@@ -1,9 +1,10 @@
-import {useMemo} from 'react';
+import {useMemo, useState} from 'react';
 import styled from '@emotion/styled';
 
+import FeedbackConfigToggle from 'sentry/components/feedback/feedbackOnboarding/feedbackConfigToggle';
 import {AuthTokenGeneratorProvider} from 'sentry/components/onboarding/gettingStartedDoc/authTokenGenerator';
 import type {OnboardingLayoutProps} from 'sentry/components/onboarding/gettingStartedDoc/onboardingLayout';
-import {Step} from 'sentry/components/onboarding/gettingStartedDoc/step';
+import {Step, StepType} from 'sentry/components/onboarding/gettingStartedDoc/step';
 import type {DocsParams} from 'sentry/components/onboarding/gettingStartedDoc/types';
 import {useSourcePackageRegistries} from 'sentry/components/onboarding/gettingStartedDoc/useSourcePackageRegistries';
 import {useUrlPlatformOptions} from 'sentry/components/onboarding/platformOptionsControl';
@@ -21,6 +22,10 @@ export function FeedbackOnboardingLayout({
   configType = 'onboarding',
 }: OnboardingLayoutProps) {
   const organization = useOrganization();
+
+  const [email, setEmail] = useState(true);
+  const [name, setName] = useState(true);
+
   const {isLoading: isLoadingRegistry, data: registryData} =
     useSourcePackageRegistries(organization);
   const selectedOptions = useUrlPlatformOptions(docsConfig.platformOptions);
@@ -44,6 +49,10 @@ export function FeedbackOnboardingLayout({
       },
       platformOptions: selectedOptions,
       newOrg,
+      feedbackOptions: {
+        email,
+        name,
+      },
     };
 
     return {
@@ -63,6 +72,8 @@ export function FeedbackOnboardingLayout({
     registryData,
     selectedOptions,
     configType,
+    email,
+    name,
   ]);
 
   return (
@@ -70,9 +81,26 @@ export function FeedbackOnboardingLayout({
       <Wrapper>
         {introduction && <Introduction>{introduction}</Introduction>}
         <Steps>
-          {steps.map(step => (
-            <Step key={step.title ?? step.type} {...step} />
-          ))}
+          {steps.map(step =>
+            step.type === StepType.CONFIGURE && configType === 'feedbackOnboardingNpm' ? (
+              <Step
+                key={step.title ?? step.type}
+                {...{
+                  ...step,
+                  codeHeader: (
+                    <FeedbackConfigToggle
+                      emailToggle={email}
+                      nameToggle={name}
+                      onEmailToggle={() => setEmail(!email)}
+                      onNameToggle={() => setName(!name)}
+                    />
+                  ),
+                }}
+              />
+            ) : (
+              <Step key={step.title ?? step.type} {...step} />
+            )
+          )}
         </Steps>
       </Wrapper>
     </AuthTokenGeneratorProvider>

+ 4 - 0
static/app/components/onboarding/gettingStartedDoc/types.ts

@@ -48,6 +48,10 @@ export interface DocsParams<
   projectSlug: Project['slug'];
   sourcePackageRegistries: {isLoading: boolean; data?: ReleaseRegistrySdk};
   cdn?: string;
+  feedbackOptions?: {
+    email?: boolean;
+    name?: boolean;
+  };
   newOrg?: boolean;
   replayOptions?: {
     block?: boolean;

+ 22 - 2
static/app/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding.tsx

@@ -4,9 +4,10 @@ import {t, tct} from 'sentry/locale';
 
 export const getFeedbackConfigureDescription = ({link}: {link: string}) =>
   tct(
-    'To set up the integration, add the following to your Sentry initialization. There are many options you can pass to the [code:integrations] constructor. Learn more about configuring User Feedback by reading the [link:configuration docs].',
+    'To set up the integration, add the following to your Sentry initialization. There are many options you can pass to the [code:integrations] constructor to customize your form. [break] [break] You can even link the widget to a custom button if you don’t want to use our autoinjected floating button. Learn more about configuring User Feedback by reading the [link:configuration docs].',
     {
       code: <code />,
+      break: <br />,
       link: <ExternalLink href={link} />,
     }
   );
@@ -14,9 +15,11 @@ export const getFeedbackConfigureDescription = ({link}: {link: string}) =>
 export const getFeedbackSDKSetupSnippet = ({
   importStatement,
   dsn,
+  feedbackOptions,
 }: {
   dsn: string;
   importStatement: string;
+  feedbackOptions?: {email?: boolean; name?: boolean};
 }) =>
   `${importStatement}
 
@@ -26,7 +29,7 @@ export const getFeedbackSDKSetupSnippet = ({
       Sentry.feedbackIntegration({
 // Additional SDK configuration goes in here, for example:
 colorScheme: "light",
-}),
+${getFeedbackConfigOptions(feedbackOptions)}}),
     ],
   });`;
 
@@ -55,3 +58,20 @@ export function FeedbackOnboardingWebApiBanner() {
     </Alert>
   );
 }
+
+export const getFeedbackConfigOptions = ({
+  name,
+  email,
+}: {
+  email?: boolean;
+  name?: boolean;
+} = {}) => {
+  const options: string[] = [];
+  if (name) {
+    options.push('isNameRequired: true,');
+  }
+  if (email) {
+    options.push('isEmailRequired: true,');
+  }
+  return options.join('\n');
+};

+ 5 - 2
static/app/gettingStartedDocs/capacitor/capacitor.tsx

@@ -8,7 +8,10 @@ import type {
   PlatformOption,
 } from 'sentry/components/onboarding/gettingStartedDoc/types';
 import {getUploadSourceMapsStep} from 'sentry/components/onboarding/gettingStartedDoc/utils';
-import {getFeedbackConfigureDescription} from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding';
+import {
+  getFeedbackConfigOptions,
+  getFeedbackConfigureDescription,
+} from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding';
 import {
   getReplayConfigOptions,
   getReplayConfigureDescription,
@@ -81,7 +84,7 @@ const getSentryInitLayout = (params: Params, siblingOption: string): string => {
         Sentry.feedbackIntegration({
 // Additional SDK configuration goes in here, for example:
 colorScheme: "light",
-}),`
+${getFeedbackConfigOptions(params.feedbackOptions)}}),`
       : ''
   }${
     params.isReplaySelected

+ 1 - 0
static/app/gettingStartedDocs/electron/electron.tsx

@@ -280,6 +280,7 @@ const feedbackOnboarding: OnboardingConfig = {
               code: getFeedbackSDKSetupSnippet({
                 importStatement: `import * as Sentry from "@sentry/electron/renderer";`,
                 dsn: params.dsn,
+                feedbackOptions: params.feedbackOptions,
               }),
             },
           ],

+ 5 - 2
static/app/gettingStartedDocs/javascript/angular.tsx

@@ -8,7 +8,10 @@ import type {
   PlatformOption,
 } from 'sentry/components/onboarding/gettingStartedDoc/types';
 import {getUploadSourceMapsStep} from 'sentry/components/onboarding/gettingStartedDoc/utils';
-import {getFeedbackConfigureDescription} from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding';
+import {
+  getFeedbackConfigOptions,
+  getFeedbackConfigureDescription,
+} from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding';
 import {getJSMetricsOnboarding} from 'sentry/components/onboarding/gettingStartedDoc/utils/metricsOnboarding';
 import {
   getReplayConfigOptions,
@@ -216,7 +219,7 @@ function getSdkSetupSnippet(params: Params) {
         Sentry.feedbackIntegration({
 // Additional SDK configuration goes in here, for example:
 colorScheme: "light",
-}),`
+${getFeedbackConfigOptions(params.feedbackOptions)}}),`
         : ''
     }${
       params.isReplaySelected

+ 1 - 0
static/app/gettingStartedDocs/javascript/astro.tsx

@@ -262,6 +262,7 @@ const feedbackOnboarding: OnboardingConfig = {
               code: getFeedbackSDKSetupSnippet({
                 importStatement: `import * as Sentry from "@sentry/astro";`,
                 dsn: params.dsn,
+                feedbackOptions: params.feedbackOptions,
               }),
             },
           ],

+ 5 - 2
static/app/gettingStartedDocs/javascript/ember.tsx

@@ -7,7 +7,10 @@ import type {
   OnboardingConfig,
 } from 'sentry/components/onboarding/gettingStartedDoc/types';
 import {getUploadSourceMapsStep} from 'sentry/components/onboarding/gettingStartedDoc/utils';
-import {getFeedbackConfigureDescription} from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding';
+import {
+  getFeedbackConfigOptions,
+  getFeedbackConfigureDescription,
+} from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding';
 import {getJSMetricsOnboarding} from 'sentry/components/onboarding/gettingStartedDoc/utils/metricsOnboarding';
 import {
   getReplayConfigOptions,
@@ -38,7 +41,7 @@ Sentry.init({
         Sentry.feedbackIntegration({
 // Additional SDK configuration goes in here, for example:
 colorScheme: "light",
-}),`
+${getFeedbackConfigOptions(params.feedbackOptions)}}),`
       : ''
   }
 ],${

+ 5 - 2
static/app/gettingStartedDocs/javascript/gatsby.tsx

@@ -9,7 +9,10 @@ import type {
   OnboardingConfig,
 } from 'sentry/components/onboarding/gettingStartedDoc/types';
 import {getUploadSourceMapsStep} from 'sentry/components/onboarding/gettingStartedDoc/utils';
-import {getFeedbackConfigureDescription} from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding';
+import {
+  getFeedbackConfigOptions,
+  getFeedbackConfigureDescription,
+} from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding';
 import {getJSMetricsOnboarding} from 'sentry/components/onboarding/gettingStartedDoc/utils/metricsOnboarding';
 import {
   getReplayConfigOptions,
@@ -35,7 +38,7 @@ Sentry.init({
         Sentry.feedbackIntegration({
 // Additional SDK configuration goes in here, for example:
 colorScheme: "light",
-}),`
+${getFeedbackConfigOptions(params.feedbackOptions)}}),`
       : ''
   }${
     params.isReplaySelected

Some files were not shown because too many files changed in this diff