Browse Source

feat(replays): add toggles for block media and mask text (#62086)

This PR adds two toggles, Mask All Text and Block All Media, next to the
replay configuration step for both JS Loader and NPM instructions.

Also fixes a bug where some instructions were not rendering correctly
based on the `?mode=` in the URL.

Closes https://github.com/getsentry/sentry/issues/61071


https://github.com/getsentry/sentry/assets/56095982/77082333-a20f-4235-bd5a-7a7827774c3e
Michelle Zhang 1 year ago
parent
commit
97c11deac5

+ 38 - 4
static/app/components/onboarding/gettingStartedDoc/onboardingLayout.tsx

@@ -1,4 +1,4 @@
-import {Fragment, useMemo} from 'react';
+import {Fragment, useMemo, useState} from 'react';
 import styled from '@emotion/styled';
 import styled from '@emotion/styled';
 
 
 import HookOrDefault from 'sentry/components/hookOrDefault';
 import HookOrDefault from 'sentry/components/hookOrDefault';
@@ -6,6 +6,7 @@ import ExternalLink from 'sentry/components/links/externalLink';
 import List from 'sentry/components/list';
 import List from 'sentry/components/list';
 import ListItem from 'sentry/components/list/listItem';
 import ListItem from 'sentry/components/list/listItem';
 import {AuthTokenGeneratorProvider} from 'sentry/components/onboarding/gettingStartedDoc/authTokenGenerator';
 import {AuthTokenGeneratorProvider} from 'sentry/components/onboarding/gettingStartedDoc/authTokenGenerator';
+import ReplayConfigToggle from 'sentry/components/onboarding/gettingStartedDoc/replayConfigToggle';
 import {Step} from 'sentry/components/onboarding/gettingStartedDoc/step';
 import {Step} from 'sentry/components/onboarding/gettingStartedDoc/step';
 import {
 import {
   ConfigType,
   ConfigType,
@@ -60,6 +61,8 @@ export function OnboardingLayout({
   const {isLoading: isLoadingRegistry, data: registryData} =
   const {isLoading: isLoadingRegistry, data: registryData} =
     useSourcePackageRegistries(organization);
     useSourcePackageRegistries(organization);
   const selectedOptions = useUrlPlatformOptions(docsConfig.platformOptions);
   const selectedOptions = useUrlPlatformOptions(docsConfig.platformOptions);
+  const [mask, setMask] = useState(true);
+  const [block, setBlock] = useState(true);
 
 
   const {platformOptions} = docsConfig;
   const {platformOptions} = docsConfig;
 
 
@@ -84,6 +87,8 @@ export function OnboardingLayout({
       },
       },
       platformOptions: selectedOptions,
       platformOptions: selectedOptions,
       newOrg,
       newOrg,
+      mask,
+      block,
     };
     };
 
 
     return {
     return {
@@ -109,6 +114,8 @@ export function OnboardingLayout({
     registryData,
     registryData,
     selectedOptions,
     selectedOptions,
     configType,
     configType,
+    mask,
+    block,
   ]);
   ]);
 
 
   return (
   return (
@@ -131,9 +138,36 @@ export function OnboardingLayout({
         </Header>
         </Header>
         <Divider withBottomMargin />
         <Divider withBottomMargin />
         <Steps>
         <Steps>
-          {steps.map(step => (
-            <Step key={step.title ?? step.type} {...step} />
-          ))}
+          {steps.map(step =>
+            step.isOptional && step.isReplayConfigStep ? (
+              <Step
+                key={step.title ?? step.type}
+                {...{
+                  ...step,
+                  additionalInfo: (
+                    <ReplayConfigToggle
+                      blockToggle={block}
+                      maskToggle={mask}
+                      onBlockToggle={() => setBlock(!block)}
+                      onMaskToggle={() => setMask(!mask)}
+                    />
+                  ),
+                }}
+              />
+            ) : (
+              <Fragment key={step.title ?? step.type}>
+                <Step key={step.title ?? step.type} {...step} />
+                {step.isReplayConfigStep ? (
+                  <ReplayConfigToggle
+                    blockToggle={block}
+                    maskToggle={mask}
+                    onBlockToggle={() => setBlock(!block)}
+                    onMaskToggle={() => setMask(!mask)}
+                  />
+                ) : null}
+              </Fragment>
+            )
+          )}
         </Steps>
         </Steps>
         {nextSteps.length > 0 && (
         {nextSteps.length > 0 && (
           <Fragment>
           <Fragment>

+ 44 - 0
static/app/components/onboarding/gettingStartedDoc/replayConfigToggle.tsx

@@ -0,0 +1,44 @@
+import styled from '@emotion/styled';
+
+import Switch from 'sentry/components/switchButton';
+import {t} from 'sentry/locale';
+import {space} from 'sentry/styles/space';
+
+function ReplayConfigToggle({
+  maskToggle,
+  onMaskToggle,
+  blockToggle,
+  onBlockToggle,
+}: {
+  blockToggle: boolean;
+  maskToggle: boolean;
+  onBlockToggle: () => void;
+  onMaskToggle: () => void;
+}) {
+  return (
+    <SwitchWrapper>
+      <SwitchItem htmlFor="mask">
+        {t('Mask All Text')}
+        <Switch id="mask" toggle={onMaskToggle} size="lg" isActive={maskToggle} />
+      </SwitchItem>
+      <SwitchItem htmlFor="block">
+        {t('Block All Media')}
+        <Switch id="block" toggle={onBlockToggle} size="lg" isActive={blockToggle} />
+      </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)};
+`;
+
+export default ReplayConfigToggle;

+ 1 - 0
static/app/components/onboarding/gettingStartedDoc/step.tsx

@@ -129,6 +129,7 @@ interface BaseStepProps {
    * Whether the step is optional
    * Whether the step is optional
    */
    */
   isOptional?: boolean;
   isOptional?: boolean;
+  isReplayConfigStep?: boolean;
 }
 }
 interface StepPropsWithTitle extends BaseStepProps {
 interface StepPropsWithTitle extends BaseStepProps {
   title: string;
   title: string;

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

@@ -46,7 +46,9 @@ export interface DocsParams<
   projectId: Project['id'];
   projectId: Project['id'];
   projectSlug: Project['slug'];
   projectSlug: Project['slug'];
   sourcePackageRegistries: {isLoading: boolean; data?: ReleaseRegistrySdk};
   sourcePackageRegistries: {isLoading: boolean; data?: ReleaseRegistrySdk};
+  block?: boolean;
   cdn?: string;
   cdn?: string;
+  mask?: boolean;
   newOrg?: boolean;
   newOrg?: boolean;
 }
 }
 
 

+ 41 - 2
static/app/components/onboarding/gettingStartedDoc/utils/index.tsx

@@ -78,10 +78,16 @@ export const getReplayConfigureDescription = ({link}: {link: string}) =>
     }
     }
   );
   );
 
 
-export const getReplayJsLoaderSdkSetupSnippet = () => `
+export const getReplayJsLoaderSdkSetupSnippet = params => `
 <script>
 <script>
   Sentry.onLoad(function() {
   Sentry.onLoad(function() {
     Sentry.init({
     Sentry.init({
+      integrations: [
+        new Sentry.Replay(${getReplayConfigOptions({
+          mask: params.mask,
+          block: params.block,
+        })}),
+      ],
       // Session Replay
       // Session Replay
       replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
       replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
       replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
       replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
@@ -92,9 +98,13 @@ export const getReplayJsLoaderSdkSetupSnippet = () => `
 export const getReplaySDKSetupSnippet = ({
 export const getReplaySDKSetupSnippet = ({
   importStatement,
   importStatement,
   dsn,
   dsn,
+  mask,
+  block,
 }: {
 }: {
   dsn: string;
   dsn: string;
   importStatement: string;
   importStatement: string;
+  block?: boolean;
+  mask?: boolean;
 }) =>
 }) =>
   `${importStatement}
   `${importStatement}
 
 
@@ -102,9 +112,38 @@ export const getReplaySDKSetupSnippet = ({
     dsn: "${dsn}",
     dsn: "${dsn}",
 
 
     integrations: [
     integrations: [
-      new Sentry.Replay(),
+      new Sentry.Replay(${getReplayConfigOptions({
+        mask,
+        block,
+      })}),
     ],
     ],
     // Session Replay
     // Session Replay
     replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
     replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
     replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
     replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
   });`;
   });`;
+
+export const getReplayConfigOptions = ({
+  mask,
+  block,
+}: {
+  block?: boolean;
+  mask?: boolean;
+}) => {
+  if (mask && block) {
+    return ``;
+  }
+  if (mask) {
+    return `{
+          blockAllMedia: false,
+        }`;
+  }
+  if (block) {
+    return `{
+          maskAllText: false,
+        }`;
+  }
+  return `{
+          maskAllText: false,
+          blockAllMedia: false,
+        }`;
+};

+ 18 - 3
static/app/components/replaysOnboarding/sidebar.tsx

@@ -24,6 +24,7 @@ import TextOverflow from 'sentry/components/textOverflow';
 import {Tooltip} from 'sentry/components/tooltip';
 import {Tooltip} from 'sentry/components/tooltip';
 import {
 import {
   backend,
   backend,
+  replayFrontendPlatforms,
   replayJsLoaderInstructionsPlatformList,
   replayJsLoaderInstructionsPlatformList,
   replayPlatforms,
   replayPlatforms,
 } from 'sentry/data/platformCategories';
 } from 'sentry/data/platformCategories';
@@ -243,6 +244,18 @@ function OnboardingContent({currentProject}: {currentProject: Project}) {
     isPlatformSupported: isPlatformSupported(currentPlatform),
     isPlatformSupported: isPlatformSupported(currentPlatform),
   });
   });
 
 
+  const defaultTab =
+    currentProject && currentProject.platform && backend.includes(currentProject.platform)
+      ? 'jsLoader'
+      : 'npm';
+
+  const npmOnlyFramework =
+    currentProject &&
+    currentProject.platform &&
+    replayFrontendPlatforms
+      .filter(p => p !== 'javascript')
+      .includes(currentProject.platform);
+
   if (isLoading) {
   if (isLoading) {
     return <LoadingIndicator />;
     return <LoadingIndicator />;
   }
   }
@@ -330,9 +343,11 @@ function OnboardingContent({currentProject}: {currentProject: Project}) {
           projectId={currentProject.id}
           projectId={currentProject.id}
           activeProductSelection={[]}
           activeProductSelection={[]}
           configType={
           configType={
-            setupMode() === 'jsLoader'
-              ? 'replayOnboardingJsLoader'
-              : 'replayOnboardingNpm'
+            setupMode() === 'npm' || // switched to NPM tab
+            (!setupMode() && defaultTab === 'npm') || // default value for FE frameworks when ?mode={...} in URL is not set yet
+            npmOnlyFramework // even if '?mode=jsLoader', only show npm instructions for FE frameworks
+              ? 'replayOnboardingNpm'
+              : 'replayOnboardingJsLoader'
           }
           }
         />
         />
       ) : (
       ) : (

+ 6 - 1
static/app/gettingStartedDocs/capacitor/capacitor.tsx

@@ -6,6 +6,7 @@ import type {
   PlatformOption,
   PlatformOption,
 } from 'sentry/components/onboarding/gettingStartedDoc/types';
 } from 'sentry/components/onboarding/gettingStartedDoc/types';
 import {
 import {
+  getReplayConfigOptions,
   getReplayConfigureDescription,
   getReplayConfigureDescription,
   getUploadSourceMapsStep,
   getUploadSourceMapsStep,
 } from 'sentry/components/onboarding/gettingStartedDoc/utils';
 } from 'sentry/components/onboarding/gettingStartedDoc/utils';
@@ -74,7 +75,10 @@ const getSentryInitLayout = (params: Params, siblingOption: string): string => {
   }${
   }${
     params.isReplaySelected
     params.isReplaySelected
       ? `
       ? `
-          new ${getSiblingImportName(siblingOption)}.Replay(),`
+          new ${getSiblingImportName(siblingOption)}.Replay(${getReplayConfigOptions({
+            mask: params.mask,
+            block: params.block,
+          })}),`
       : ''
       : ''
   }
   }
   ],${
   ],${
@@ -404,6 +408,7 @@ const replayOnboarding: OnboardingConfig<PlatformOptions> = {
   configure: params => [
   configure: params => [
     {
     {
       type: StepType.CONFIGURE,
       type: StepType.CONFIGURE,
+      isReplayConfigStep: true,
       configurations: [
       configurations: [
         {
         {
           description: getReplayConfigureDescription({
           description: getReplayConfigureDescription({

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

@@ -122,6 +122,7 @@ const replayOnboarding: OnboardingConfig = {
   configure: (params: Params) => [
   configure: (params: Params) => [
     {
     {
       type: StepType.CONFIGURE,
       type: StepType.CONFIGURE,
+      isReplayConfigStep: true,
       description: getReplayConfigureDescription({
       description: getReplayConfigureDescription({
         link: 'https://docs.sentry.io/platforms/javascript/guides/electron/session-replay/',
         link: 'https://docs.sentry.io/platforms/javascript/guides/electron/session-replay/',
       }),
       }),
@@ -135,6 +136,8 @@ const replayOnboarding: OnboardingConfig = {
               code: getReplaySDKSetupSnippet({
               code: getReplaySDKSetupSnippet({
                 importStatement: `import * as Sentry from "@sentry/electron";`,
                 importStatement: `import * as Sentry from "@sentry/electron";`,
                 dsn: params.dsn,
                 dsn: params.dsn,
+                mask: params.mask,
+                block: params.block,
               }),
               }),
             },
             },
           ],
           ],

+ 6 - 1
static/app/gettingStartedDocs/javascript/angular.tsx

@@ -6,6 +6,7 @@ import type {
   PlatformOption,
   PlatformOption,
 } from 'sentry/components/onboarding/gettingStartedDoc/types';
 } from 'sentry/components/onboarding/gettingStartedDoc/types';
 import {
 import {
+  getReplayConfigOptions,
   getReplayConfigureDescription,
   getReplayConfigureDescription,
   getUploadSourceMapsStep,
   getUploadSourceMapsStep,
 } from 'sentry/components/onboarding/gettingStartedDoc/utils';
 } from 'sentry/components/onboarding/gettingStartedDoc/utils';
@@ -212,7 +213,10 @@ function getSdkSetupSnippet(params: Params) {
     }${
     }${
       params.isReplaySelected
       params.isReplaySelected
         ? `
         ? `
-          new Sentry.Replay(),`
+          new Sentry.Replay(${getReplayConfigOptions({
+            mask: params.mask,
+            block: params.block,
+          })}),`
         : ''
         : ''
     }
     }
   ],${
   ],${
@@ -270,6 +274,7 @@ const replayOnboarding: OnboardingConfig<PlatformOptions> = {
   configure: params => [
   configure: params => [
     {
     {
       type: StepType.CONFIGURE,
       type: StepType.CONFIGURE,
+      isReplayConfigStep: true,
       configurations: [
       configurations: [
         {
         {
           description: getReplayConfigureDescription({
           description: getReplayConfigureDescription({

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

@@ -200,6 +200,7 @@ const replayOnboarding: OnboardingConfig = {
   configure: (params: Params) => [
   configure: (params: Params) => [
     {
     {
       type: StepType.CONFIGURE,
       type: StepType.CONFIGURE,
+      isReplayConfigStep: true,
       description: getReplayConfigureDescription({
       description: getReplayConfigureDescription({
         link: 'https://docs.sentry.io/platforms/javascript/guides/astro/session-replay/',
         link: 'https://docs.sentry.io/platforms/javascript/guides/astro/session-replay/',
       }),
       }),
@@ -213,6 +214,8 @@ const replayOnboarding: OnboardingConfig = {
               code: getReplaySDKSetupSnippet({
               code: getReplaySDKSetupSnippet({
                 importStatement: `import * as Sentry from "@sentry/astro";`,
                 importStatement: `import * as Sentry from "@sentry/astro";`,
                 dsn: params.dsn,
                 dsn: params.dsn,
+                mask: params.mask,
+                block: params.block,
               }),
               }),
             },
             },
           ],
           ],

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