Browse Source

feat(replays): add segmented control to sidebar for npm/jsLoader instructions (#61554)

testing with only the `javascript-react` platform for now since
https://github.com/getsentry/sentry/pull/61448 & a few other platform
conversions still needs to be merged.

add a segmented control to the replay onboarding flyout sidebar, to
determine whether the NPM or JS Loader instructions should be shown.





https://github.com/getsentry/sentry/assets/56095982/531e2891-b339-4f22-bc15-494aedbe4397

relates to https://github.com/getsentry/sentry/issues/61069

note: one thing we haven't accounted for yet here: whether the user has
"Enable session replay" checked inside the project settings -> DSN ->
Loader page


![image](https://github.com/getsentry/sentry/assets/56095982/f9233ff5-8709-40be-931e-bb764d95cd9c)
Michelle Zhang 1 year ago
parent
commit
20a5ce4034

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

@@ -38,6 +38,7 @@ export type OnboardingLayoutProps = {
   projectId: Project['id'];
   projectSlug: Project['slug'];
   activeProductSelection?: ProductSolution[];
+  cdn?: string;
   configType?: ConfigType;
   newOrg?: boolean;
 };
@@ -45,6 +46,7 @@ export type OnboardingLayoutProps = {
 const EMPTY_ARRAY: never[] = [];
 
 export function OnboardingLayout({
+  cdn,
   docsConfig,
   dsn,
   platformKey,
@@ -65,6 +67,7 @@ export function OnboardingLayout({
     const doc = docsConfig[configType] ?? docsConfig.onboarding;
 
     const docParams: DocsParams<any> = {
+      cdn,
       dsn,
       organization,
       platformKey,
@@ -93,6 +96,7 @@ export function OnboardingLayout({
       nextSteps: doc.nextSteps?.(docParams) || [],
     };
   }, [
+    cdn,
     activeProductSelection,
     docsConfig,
     dsn,

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

@@ -141,6 +141,7 @@ export function SdkDocumentation({
     <OnboardingLayout
       docsConfig={docs}
       dsn={projectKeys[0].dsn.public}
+      cdn={projectKeys[0].dsn.cdn}
       activeProductSelection={activeProductSelection}
       newOrg={newOrg}
       platformKey={platform.id}

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

@@ -46,6 +46,7 @@ export interface DocsParams<
   projectId: Project['id'];
   projectSlug: Project['slug'];
   sourcePackageRegistries: {isLoading: boolean; data?: ReleaseRegistrySdk};
+  cdn?: string;
   newOrg?: boolean;
 }
 
@@ -71,7 +72,11 @@ export interface OnboardingConfig<
 export interface Docs<PlatformOptions extends BasePlatformOptions = BasePlatformOptions> {
   onboarding: OnboardingConfig<PlatformOptions>;
   platformOptions?: PlatformOptions;
-  replayOnboarding?: OnboardingConfig<PlatformOptions>;
+  replayOnboardingJsLoader?: OnboardingConfig<PlatformOptions>;
+  replayOnboardingNpm?: OnboardingConfig<PlatformOptions>;
 }
 
-export type ConfigType = 'onboarding' | 'replayOnboarding';
+export type ConfigType =
+  | 'onboarding'
+  | 'replayOnboardingNpm'
+  | 'replayOnboardingJsLoader';

+ 11 - 0
static/app/components/onboarding/gettingStartedDoc/utils.tsx

@@ -78,6 +78,17 @@ export const getReplayConfigureDescription = ({link}: {link: string}) =>
     }
   );
 
+export const getReplayJsLoaderSdkSetupSnippet = () => `
+<script>
+  Sentry.onLoad(function() {
+    Sentry.init({
+      // 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.
+      replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
+    });
+  });
+</script>`;
+
 export const getReplaySDKSetupSnippet = ({
   importStatement,
   dsn,

+ 83 - 33
static/app/components/replaysOnboarding/sidebar.tsx

@@ -14,9 +14,11 @@ import {
   generateDocKeys,
   isPlatformSupported,
 } from 'sentry/components/replaysOnboarding/utils';
+import {SegmentedControl} from 'sentry/components/segmentedControl';
 import {DocumentationWrapper} from 'sentry/components/sidebar/onboardingStep';
 import SidebarPanel from 'sentry/components/sidebar/sidebarPanel';
 import {CommonSidebarProps, SidebarPanelKey} from 'sentry/components/sidebar/types';
+import {Tooltip} from 'sentry/components/tooltip';
 import {replayPlatforms} from 'sentry/data/platformCategories';
 import platforms from 'sentry/data/platforms';
 import {t, tct} from 'sentry/locale';
@@ -27,6 +29,7 @@ import EventWaiter from 'sentry/utils/eventWaiter';
 import useApi from 'sentry/utils/useApi';
 import useOrganization from 'sentry/utils/useOrganization';
 import usePrevious from 'sentry/utils/usePrevious';
+import useUrlParams from 'sentry/utils/useUrlParams';
 
 function ReplaysOnboardingSidebar(props: CommonSidebarProps) {
   const {currentPanel, collapsed, hidePanel, orientation} = props;
@@ -35,6 +38,8 @@ function ReplaysOnboardingSidebar(props: CommonSidebarProps) {
   const isActive = currentPanel === SidebarPanelKey.REPLAYS_ONBOARDING;
   const hasProjectAccess = organization.access.includes('project:read');
 
+  const newOnboarding = organization.features.includes('session-replay-new-zero-state');
+
   const {
     projects,
     allProjects,
@@ -90,6 +95,11 @@ function ReplaysOnboardingSidebar(props: CommonSidebarProps) {
     ];
   }, [supportedProjects, unsupportedProjects]);
 
+  const {getParamValue: setupMode, setParamValue: setSetupMode} = useUrlParams(
+    'mode',
+    'npm' // this default  needs to be changed later. for backend platforms, should default to jsLoader
+  );
+
   const selectedProject = currentProject ?? projects[0] ?? allProjects[0];
   if (!isActive || !hasProjectAccess || !selectedProject) {
     return null;
@@ -104,35 +114,59 @@ function ReplaysOnboardingSidebar(props: CommonSidebarProps) {
       <TopRightBackgroundImage src={HighlightTopRightPattern} />
       <TaskList>
         <Heading>{t('Getting Started with Session Replay')}</Heading>
-        <div
-          onClick={e => {
-            // we need to stop bubbling the CompactSelect click event
-            // failing to do so will cause the sidebar panel to close
-            // the event.target will be unmounted by the time the panel listener
-            // receives the event and assume the click was outside the panel
-            e.stopPropagation();
-          }}
-        >
-          <CompactSelect
-            triggerLabel={
-              currentProject ? (
-                <StyledIdBadge
-                  project={currentProject}
-                  avatarSize={16}
-                  hideOverflow
-                  disableLink
-                />
-              ) : (
-                t('Select a project')
-              )
-            }
-            value={currentProject?.id}
-            onChange={opt => setCurrentProject(allProjects.find(p => p.id === opt.value))}
-            triggerProps={{'aria-label': currentProject?.slug}}
-            options={projectSelectOptions}
-            position="bottom-end"
-          />
-        </div>
+        <HeaderActions>
+          <div
+            onClick={e => {
+              // we need to stop bubbling the CompactSelect click event
+              // failing to do so will cause the sidebar panel to close
+              // the event.target will be unmounted by the time the panel listener
+              // receives the event and assume the click was outside the panel
+              e.stopPropagation();
+            }}
+          >
+            <CompactSelect
+              triggerLabel={
+                currentProject ? (
+                  <StyledIdBadge
+                    project={currentProject}
+                    avatarSize={16}
+                    hideOverflow
+                    disableLink
+                  />
+                ) : (
+                  t('Select a project')
+                )
+              }
+              value={currentProject?.id}
+              onChange={opt =>
+                setCurrentProject(allProjects.find(p => p.id === opt.value))
+              }
+              triggerProps={{'aria-label': currentProject?.slug}}
+              options={projectSelectOptions}
+              position="bottom-end"
+            />
+          </div>
+          {newOnboarding && (
+            <SegmentedControl
+              size="md"
+              aria-label={t('Change setup method')}
+              value={setupMode()}
+              onChange={setSetupMode}
+            >
+              <SegmentedControl.Item key="npm">
+                <StyledTooltip title={t('I have a JS Framework')} showOnlyOnOverflow>
+                  {t('I have a JS Framework')}
+                </StyledTooltip>
+              </SegmentedControl.Item>
+
+              <SegmentedControl.Item key="jsLoader">
+                <StyledTooltip title={t('I have an HTML Template')} showOnlyOnOverflow>
+                  {t('I have an HTML Template')}
+                </StyledTooltip>
+              </SegmentedControl.Item>
+            </SegmentedControl>
+          )}
+        </HeaderActions>
         <OnboardingContent currentProject={selectedProject} />
       </TaskList>
     </TaskSidebarPanel>
@@ -144,6 +178,7 @@ function OnboardingContent({currentProject}: {currentProject: Project}) {
   const organization = useOrganization();
   const previousProject = usePrevious(currentProject);
   const [received, setReceived] = useState<boolean>(false);
+  const {getParamValue: setupMode} = useUrlParams('mode');
 
   useEffect(() => {
     if (previousProject.id !== currentProject.id) {
@@ -165,8 +200,6 @@ function OnboardingContent({currentProject}: {currentProject: Project}) {
     isPlatformSupported: isPlatformSupported(currentPlatform),
   });
 
-  const newOnboarding = organization.features.includes('session-replay-new-zero-state');
-
   if (isLoading) {
     return <LoadingIndicator />;
   }
@@ -231,6 +264,8 @@ function OnboardingContent({currentProject}: {currentProject: Project}) {
     'javascript-gatsby',
     'electron',
   ];
+  const newOnboarding = organization.features.includes('session-replay-new-zero-state');
+  const showNewOnboardingUI = newOnboarding && migrated.includes(currentPlatform.id);
 
   return (
     <Fragment>
@@ -240,14 +275,18 @@ function OnboardingContent({currentProject}: {currentProject: Project}) {
           {platform: currentPlatform?.name || currentProject.slug}
         )}
       </IntroText>
-      {newOnboarding && migrated.includes(currentPlatform.id) ? (
+      {showNewOnboardingUI ? (
         <SdkDocumentation
           platform={currentPlatform}
           organization={organization}
           projectSlug={currentProject.slug}
           projectId={currentProject.id}
           activeProductSelection={[]}
-          configType="replayOnboarding"
+          configType={
+            setupMode() === 'jsLoader'
+              ? 'replayOnboardingJsLoader'
+              : 'replayOnboardingNpm'
+          }
         />
       ) : (
         docKeys.map((docKey, index) => {
@@ -392,4 +431,15 @@ const EventReceivedIndicator = styled((p: React.HTMLAttributes<HTMLDivElement>)
   color: ${p => p.theme.successText};
 `;
 
+const HeaderActions = styled('div')`
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+  gap: ${space(3)};
+`;
+
+const StyledTooltip = styled(Tooltip)`
+  ${p => p.theme.overflowEllipsis};
+`;
+
 export default ReplaysOnboardingSidebar;

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

@@ -147,7 +147,7 @@ const replayOnboarding: OnboardingConfig = {
 
 const docs: Docs = {
   onboarding,
-  replayOnboarding,
+  replayOnboardingNpm: replayOnboarding,
 };
 
 export default docs;

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

@@ -225,7 +225,7 @@ const replayOnboarding: OnboardingConfig = {
 
 const docs: Docs = {
   onboarding,
-  replayOnboarding,
+  replayOnboardingNpm: replayOnboarding,
 };
 
 export default docs;

+ 1 - 1
static/app/gettingStartedDocs/javascript/ember.tsx

@@ -188,7 +188,7 @@ const replayOnboarding: OnboardingConfig = {
 
 const docs: Docs = {
   onboarding,
-  replayOnboarding,
+  replayOnboardingNpm: replayOnboarding,
 };
 
 export default docs;

+ 1 - 1
static/app/gettingStartedDocs/javascript/gatsby.tsx

@@ -211,7 +211,7 @@ const replayOnboarding: OnboardingConfig = {
 
 const docs: Docs = {
   onboarding,
-  replayOnboarding,
+  replayOnboardingNpm: replayOnboarding,
 };
 
 export default docs;

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

@@ -183,7 +183,7 @@ const replayOnboarding: OnboardingConfig = {
 
 const docs: Docs = {
   onboarding,
-  replayOnboarding,
+  replayOnboardingNpm: replayOnboarding,
 };
 
 export default docs;

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