Browse Source

ref(onboarding): fix initial project state (#75094)

Fixes an onboarding sidebar bug where multiple projects or no projects
being selected leads the wrong platform to be displayed in the sidebar.

Specifically on issues, if you have multiple projects selected and click
into a specific event details, the global project selection will
persist, and there's a chance the wrong platform will be displayed when
you jump into the replay onboarding side. This PR sets the URL `project`
param so that it's always the platform of the event being showed.

Before: sidebar displays instructions for bun when the platform is go,
since bun is the first project in the global selection:


https://github.com/user-attachments/assets/0b98ff2f-04fc-4bc5-929d-fe4c69669a19


After: sidebar displays correct instructions for the platform even
though there are many projects in the global selection:



https://github.com/user-attachments/assets/f4c9155c-1448-4626-b1bc-2dc8d5769705
Michelle Zhang 7 months ago
parent
commit
927a1424de

+ 10 - 1
static/app/components/events/eventReplay/index.tsx

@@ -1,4 +1,4 @@
-import {lazy} from 'react';
+import {lazy, useEffect} from 'react';
 
 import ErrorBoundary from 'sentry/components/errorBoundary';
 import {ReplayClipSection} from 'sentry/components/events/eventReplay/replayClipSection';
@@ -8,6 +8,7 @@ import type {Group} from 'sentry/types/group';
 import useEventCanShowReplayUpsell from 'sentry/utils/event/useEventCanShowReplayUpsell';
 import {getReplayIdFromEvent} from 'sentry/utils/replays/getReplayIdFromEvent';
 import {useHaveSelectedProjectsSentAnyReplayEvents} from 'sentry/utils/replays/hooks/useReplayOnboarding';
+import useUrlParams from 'sentry/utils/useUrlParams';
 
 interface Props {
   event: Event;
@@ -26,6 +27,14 @@ export default function EventReplay({event, group, projectSlug}: Props) {
     projectSlug,
   });
 
+  const {setParamValue: setProjectId} = useUrlParams('project');
+
+  useEffect(() => {
+    if (canShowUpsell) {
+      setProjectId(upsellProjectId);
+    }
+  }, [upsellProjectId, setProjectId, canShowUpsell]);
+
   if (replayId) {
     return <ReplayClipSection event={event} replayId={replayId} group={group} />;
   }

+ 4 - 4
static/app/components/onboarding/gettingStartedDoc/utils/useCurrentProjectState.spec.tsx

@@ -89,7 +89,7 @@ describe('useCurrentProjectState', () => {
     expect(result.current.currentProject).toBe(undefined);
   });
 
-  it('should return currentProject=undefined whenproject url param is present and currentPanel != targetPanel', () => {
+  it('should return currentProject=undefined when project url param is present and currentPanel != targetPanel', () => {
     ProjectsStore.loadInitialData([javascript, angular]);
     mockPageFilterStore([javascript, angular]);
     const {result} = renderHook(useCurrentProjectState, {
@@ -99,7 +99,7 @@ describe('useCurrentProjectState', () => {
         onboardingPlatforms: customMetricOnboardingPlatforms,
         allPlatforms: customMetricPlatforms,
       },
-      wrapper: createWrapper(angular.slug),
+      wrapper: createWrapper(angular.id),
     });
     expect(result.current.currentProject).toBe(undefined);
   });
@@ -129,9 +129,9 @@ describe('useCurrentProjectState', () => {
         onboardingPlatforms: customMetricOnboardingPlatforms,
         allPlatforms: customMetricPlatforms,
       },
-      wrapper: createWrapper(angular.slug),
+      wrapper: createWrapper(javascript.id),
     });
-    expect(result.current.currentProject).toBe(angular);
+    expect(result.current.currentProject).toBe(javascript);
   });
 
   it('should return the first project if global selection does not have onboarding', () => {

+ 6 - 7
static/app/components/onboarding/gettingStartedDoc/utils/useCurrentProjectState.tsx

@@ -5,8 +5,8 @@ import type {SidebarPanelKey} from 'sentry/components/sidebar/types';
 import PageFiltersStore from 'sentry/stores/pageFiltersStore';
 import {useLegacyStore} from 'sentry/stores/useLegacyStore';
 import type {PlatformKey, Project} from 'sentry/types/project';
-import {useParams} from 'sentry/utils/useParams';
 import useProjects from 'sentry/utils/useProjects';
+import useUrlParams from 'sentry/utils/useUrlParams';
 
 type Props = {
   allPlatforms: readonly PlatformKey[];
@@ -21,12 +21,11 @@ function useCurrentProjectState({
   onboardingPlatforms,
   allPlatforms,
 }: Props) {
-  const params = useParams<{projectId?: string}>();
-  const projectSlug = params.projectId;
   const {projects, initiallyLoaded: projectsLoaded} = useProjects();
   const {selection, isReady} = useLegacyStore(PageFiltersStore);
   const [currentProject, setCurrentProject] = useState<Project | undefined>(undefined);
-
+  const {getParamValue: projectIds} = useUrlParams('project');
+  const projectId = projectIds()?.split('&').at(0);
   const isActive = currentPanel === targetPanel;
 
   // Projects with onboarding instructions
@@ -55,8 +54,8 @@ function useCurrentProjectState({
       return;
     }
 
-    if (projectSlug) {
-      setCurrentProject(projects.find(p => p.slug === projectSlug) ?? undefined);
+    if (projectId) {
+      setCurrentProject(projects.find(p => p.id === projectId) ?? undefined);
       return;
     }
 
@@ -100,7 +99,7 @@ function useCurrentProjectState({
     selection.projects,
     projectsWithOnboarding,
     supportedProjects,
-    projectSlug,
+    projectId,
   ]);
 
   return {