Browse Source

feat(mobile-ui): Expose UI module in sidebar (#69801)

This just exposes the UI (previously titled Responsiveness) module in
the sidebar. I've changed the page filters to use our standard ones and
done a small refactor to share the same landing page code as the app
starts module.
Nar Saynorath 10 months ago
parent
commit
520bab7a50

+ 13 - 0
static/app/components/sidebar/index.tsx

@@ -348,6 +348,19 @@ function Sidebar() {
                   icon={<SubitemDot collapsed />}
                 />
               </Feature>
+              <Feature
+                features={['spans-first-ui', 'starfish-mobile-ui-module']}
+                organization={organization}
+              >
+                <SidebarItem
+                  {...sidebarItemProps}
+                  label={t('Mobile UI')}
+                  to={`/organizations/${organization.slug}/performance/mobile/ui/`}
+                  id="performance-mobile-ui"
+                  icon={<SubitemDot collapsed />}
+                  isAlpha
+                />
+              </Feature>
               <Feature features="spans-first-ui">
                 <SidebarItem
                   {...sidebarItemProps}

+ 5 - 7
static/app/routes.tsx

@@ -1619,6 +1619,11 @@ function buildRoutes() {
             )}
           />
         </Route>
+        <Route path="ui/">
+          <IndexRoute
+            component={make(() => import('sentry/views/performance/mobile/ui'))}
+          />
+        </Route>
       </Route>
       <Route path="traces/">
         <IndexRoute component={make(() => import('sentry/views/performance/traces'))} />
@@ -1725,13 +1730,6 @@ function buildRoutes() {
           )}
         />
       </Route>
-      <Route path="responsiveness/">
-        <IndexRoute
-          component={make(
-            () => import('sentry/views/starfish/modules/mobile/responsiveness')
-          )}
-        />
-      </Route>
     </Route>
   );
 

+ 11 - 104
static/app/views/performance/mobile/appStarts/index.tsx

@@ -1,118 +1,25 @@
-import {useCallback} from 'react';
-import {browserHistory} from 'react-router';
-import styled from '@emotion/styled';
-import omit from 'lodash/omit';
-
 import Feature from 'sentry/components/acl/feature';
-import {Breadcrumbs} from 'sentry/components/breadcrumbs';
-import ButtonBar from 'sentry/components/buttonBar';
-import ErrorBoundary from 'sentry/components/errorBoundary';
-import FeedbackWidgetButton from 'sentry/components/feedback/widget/feedbackWidgetButton';
-import * as Layout from 'sentry/components/layouts/thirds';
-import {DatePageFilter} from 'sentry/components/organizations/datePageFilter';
-import {EnvironmentPageFilter} from 'sentry/components/organizations/environmentPageFilter';
-import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
-import PageFiltersContainer from 'sentry/components/organizations/pageFilters/container';
-import {ProjectPageFilter} from 'sentry/components/organizations/projectPageFilter';
-import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
-import {t} from 'sentry/locale';
-import {space} from 'sentry/styles/space';
-import {PageAlert, PageAlertProvider} from 'sentry/utils/performance/contexts/pageAlert';
-import {useLocation} from 'sentry/utils/useLocation';
 import useOrganization from 'sentry/utils/useOrganization';
-import {normalizeUrl} from 'sentry/utils/withDomainRequired';
-import {useOnboardingProject} from 'sentry/views/performance/browser/webVitals/utils/useOnboardingProject';
 import AppStartup from 'sentry/views/performance/mobile/appStarts/screens';
 import {StartTypeSelector} from 'sentry/views/performance/mobile/appStarts/screenSummary/startTypeSelector';
-import Onboarding from 'sentry/views/performance/onboarding';
-import {PlatformCompatibilityChecker} from 'sentry/views/performance/platformCompatibilityChecker';
-import {ReleaseComparisonSelector} from 'sentry/views/starfish/components/releaseSelector';
+import ScreensTemplate from 'sentry/views/performance/mobile/components/screensTemplate';
 import {ROUTE_NAMES} from 'sentry/views/starfish/utils/routeNames';
 
 export default function InitializationModule() {
   const organization = useOrganization();
-  const onboardingProject = useOnboardingProject();
-  const location = useLocation();
-
-  const handleProjectChange = useCallback(() => {
-    browserHistory.replace({
-      ...location,
-      query: {
-        ...omit(location.query, ['primaryRelease', 'secondaryRelease']),
-      },
-    });
-  }, [location]);
 
   return (
     <Feature features="spans-first-ui" organization={organization}>
-      <SentryDocumentTitle title={ROUTE_NAMES['app-startup']} orgSlug={organization.slug}>
-        <Layout.Page>
-          <PageAlertProvider>
-            <Layout.Header>
-              <Layout.HeaderContent>
-                <Breadcrumbs
-                  crumbs={[
-                    {
-                      label: t('Performance'),
-                      to: normalizeUrl(
-                        `/organizations/${organization.slug}/performance/`
-                      ),
-                      preservePageFilters: true,
-                    },
-                    {
-                      label: ROUTE_NAMES['app-startup'],
-                    },
-                  ]}
-                />
-                <Layout.Title>{ROUTE_NAMES['app-startup']}</Layout.Title>
-              </Layout.HeaderContent>
-              <Layout.HeaderActions>
-                <ButtonBar gap={1}>
-                  <FeedbackWidgetButton />
-                </ButtonBar>
-              </Layout.HeaderActions>
-            </Layout.Header>
-
-            <Layout.Body>
-              <Layout.Main fullWidth>
-                <PageFiltersContainer>
-                  <Container>
-                    <PageFilterBar condensed>
-                      <ProjectPageFilter onChange={handleProjectChange} />
-                      <EnvironmentPageFilter />
-                      <DatePageFilter />
-                    </PageFilterBar>
-                    <ReleaseComparisonSelector />
-                    <StartTypeSelector />
-                  </Container>
-                </PageFiltersContainer>
-                <PageAlert />
-                <ErrorBoundary mini>
-                  <PlatformCompatibilityChecker
-                    compatibleSDKNames={['sentry.cocoa', 'sentry.java.android']}
-                    docsUrl="https://docs.sentry.io/product/performance/mobile-vitals/app-starts/#minimum-sdk-requirements"
-                  >
-                    {onboardingProject && (
-                      <Onboarding
-                        organization={organization}
-                        project={onboardingProject}
-                      />
-                    )}
-                    {!onboardingProject && <AppStartup chartHeight={200} />}
-                  </PlatformCompatibilityChecker>
-                </ErrorBoundary>
-              </Layout.Main>
-            </Layout.Body>
-          </PageAlertProvider>
-        </Layout.Page>
-      </SentryDocumentTitle>
+      <ScreensTemplate
+        additionalSelectors={<StartTypeSelector />}
+        compatibilityProps={{
+          compatibleSDKNames: ['sentry.cocoa', 'sentry.java.android'],
+          docsUrl:
+            'https://docs.sentry.io/product/performance/mobile-vitals/app-starts/#minimum-sdk-requirements',
+        }}
+        content={<AppStartup chartHeight={200} />}
+        title={ROUTE_NAMES['app-startup']}
+      />
     </Feature>
   );
 }
-
-const Container = styled('div')`
-  display: flex;
-  gap: ${space(2)};
-  margin-bottom: ${space(2)};
-  flex-wrap: wrap;
-`;

+ 119 - 0
static/app/views/performance/mobile/components/screensTemplate.tsx

@@ -0,0 +1,119 @@
+import {type ReactNode, useCallback} from 'react';
+import {browserHistory} from 'react-router';
+import styled from '@emotion/styled';
+import omit from 'lodash/omit';
+
+import {Breadcrumbs} from 'sentry/components/breadcrumbs';
+import ButtonBar from 'sentry/components/buttonBar';
+import ErrorBoundary from 'sentry/components/errorBoundary';
+import FeedbackWidgetButton from 'sentry/components/feedback/widget/feedbackWidgetButton';
+import * as Layout from 'sentry/components/layouts/thirds';
+import {DatePageFilter} from 'sentry/components/organizations/datePageFilter';
+import {EnvironmentPageFilter} from 'sentry/components/organizations/environmentPageFilter';
+import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
+import PageFiltersContainer from 'sentry/components/organizations/pageFilters/container';
+import {ProjectPageFilter} from 'sentry/components/organizations/projectPageFilter';
+import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
+import {t} from 'sentry/locale';
+import {space} from 'sentry/styles/space';
+import {PageAlert, PageAlertProvider} from 'sentry/utils/performance/contexts/pageAlert';
+import {useLocation} from 'sentry/utils/useLocation';
+import useOrganization from 'sentry/utils/useOrganization';
+import {normalizeUrl} from 'sentry/utils/withDomainRequired';
+import {useOnboardingProject} from 'sentry/views/performance/browser/webVitals/utils/useOnboardingProject';
+import Onboarding from 'sentry/views/performance/onboarding';
+import {PlatformCompatibilityChecker} from 'sentry/views/performance/platformCompatibilityChecker';
+import {ReleaseComparisonSelector} from 'sentry/views/starfish/components/releaseSelector';
+
+type ScreensTemplateProps = {
+  compatibilityProps: {
+    compatibleSDKNames: string[];
+    docsUrl: string;
+  };
+  content: ReactNode;
+  title: string;
+  additionalSelectors?: ReactNode;
+};
+
+export default function ScreensTemplate({
+  title,
+  additionalSelectors,
+  compatibilityProps,
+  content,
+}: ScreensTemplateProps) {
+  const organization = useOrganization();
+  const onboardingProject = useOnboardingProject();
+  const location = useLocation();
+
+  const handleProjectChange = useCallback(() => {
+    browserHistory.replace({
+      ...location,
+      query: {
+        ...omit(location.query, ['primaryRelease', 'secondaryRelease']),
+      },
+    });
+  }, [location]);
+
+  return (
+    <SentryDocumentTitle title={title} orgSlug={organization.slug}>
+      <Layout.Page>
+        <PageAlertProvider>
+          <Layout.Header>
+            <Layout.HeaderContent>
+              <Breadcrumbs
+                crumbs={[
+                  {
+                    label: t('Performance'),
+                    to: normalizeUrl(`/organizations/${organization.slug}/performance/`),
+                    preservePageFilters: true,
+                  },
+                  {
+                    label: title,
+                  },
+                ]}
+              />
+              <Layout.Title>{title}</Layout.Title>
+            </Layout.HeaderContent>
+            <Layout.HeaderActions>
+              <ButtonBar gap={1}>
+                <FeedbackWidgetButton />
+              </ButtonBar>
+            </Layout.HeaderActions>
+          </Layout.Header>
+
+          <Layout.Body>
+            <Layout.Main fullWidth>
+              <PageFiltersContainer>
+                <Container>
+                  <PageFilterBar condensed>
+                    <ProjectPageFilter onChange={handleProjectChange} />
+                    <EnvironmentPageFilter />
+                    <DatePageFilter />
+                  </PageFilterBar>
+                  <ReleaseComparisonSelector />
+                  {additionalSelectors}
+                </Container>
+              </PageFiltersContainer>
+              <PageAlert />
+              <ErrorBoundary mini>
+                <PlatformCompatibilityChecker {...compatibilityProps}>
+                  {onboardingProject && (
+                    <Onboarding organization={organization} project={onboardingProject} />
+                  )}
+                  {!onboardingProject && content}
+                </PlatformCompatibilityChecker>
+              </ErrorBoundary>
+            </Layout.Main>
+          </Layout.Body>
+        </PageAlertProvider>
+      </Layout.Page>
+    </SentryDocumentTitle>
+  );
+}
+
+const Container = styled('div')`
+  display: flex;
+  gap: ${space(2)};
+  margin-bottom: ${space(2)};
+  flex-wrap: wrap;
+`;

+ 25 - 0
static/app/views/performance/mobile/ui/index.tsx

@@ -0,0 +1,25 @@
+import Feature from 'sentry/components/acl/feature';
+import useOrganization from 'sentry/utils/useOrganization';
+import ScreensTemplate from 'sentry/views/performance/mobile/components/screensTemplate';
+import {ScreensView, YAxis} from 'sentry/views/performance/mobile/screenload/screens';
+import {ROUTE_NAMES} from 'sentry/views/starfish/utils/routeNames';
+
+export default function ResponsivenessModule() {
+  const organization = useOrganization();
+
+  return (
+    <Feature
+      features={['spans-first-ui', 'starfish-mobile-ui-module']}
+      organization={organization}
+    >
+      <ScreensTemplate
+        content={<ScreensView yAxes={[YAxis.SLOW_FRAME_RATE, YAxis.FROZEN_FRAME_RATE]} />}
+        compatibilityProps={{
+          compatibleSDKNames: ['sentry.cocoa', 'sentry.java.android'],
+          docsUrl: 'www.docs.sentry.io', // TODO: Add real docs URL
+        }}
+        title={ROUTE_NAMES.mobileUI}
+      />
+    </Feature>
+  );
+}

+ 0 - 60
static/app/views/starfish/modules/mobile/responsiveness.tsx

@@ -1,60 +0,0 @@
-import styled from '@emotion/styled';
-
-import * as Layout from 'sentry/components/layouts/thirds';
-import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
-import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
-import {space} from 'sentry/styles/space';
-import {PageAlert, PageAlertProvider} from 'sentry/utils/performance/contexts/pageAlert';
-import useOrganization from 'sentry/utils/useOrganization';
-import {ScreensView, YAxis} from 'sentry/views/performance/mobile/screenload/screens';
-import StarfishDatePicker from 'sentry/views/starfish/components/datePicker';
-import {ReleaseComparisonSelector} from 'sentry/views/starfish/components/releaseSelector';
-import {StarfishPageFiltersContainer} from 'sentry/views/starfish/components/starfishPageFiltersContainer';
-import {StarfishProjectSelector} from 'sentry/views/starfish/components/starfishProjectSelector';
-import {ROUTE_NAMES} from 'sentry/views/starfish/utils/routeNames';
-
-export default function ResponsivenessModule() {
-  const organization = useOrganization();
-
-  return (
-    <SentryDocumentTitle title={ROUTE_NAMES.responsiveness} orgSlug={organization.slug}>
-      <Layout.Page>
-        <PageAlertProvider>
-          <Layout.Header>
-            <Layout.HeaderContent>
-              <Layout.Title>{ROUTE_NAMES.responsiveness}</Layout.Title>
-            </Layout.HeaderContent>
-          </Layout.Header>
-
-          <Layout.Body>
-            <Layout.Main fullWidth>
-              <PageAlert />
-              <StarfishPageFiltersContainer>
-                <SearchContainerWithFilterAndMetrics>
-                  <PageFilterBar condensed>
-                    <StarfishProjectSelector />
-                    <StarfishDatePicker />
-                  </PageFilterBar>
-                  <ReleaseComparisonSelector />
-                </SearchContainerWithFilterAndMetrics>
-                <ScreensView yAxes={[YAxis.SLOW_FRAME_RATE, YAxis.FROZEN_FRAME_RATE]} />
-              </StarfishPageFiltersContainer>
-            </Layout.Main>
-          </Layout.Body>
-        </PageAlertProvider>
-      </Layout.Page>
-    </SentryDocumentTitle>
-  );
-}
-
-const SearchContainerWithFilterAndMetrics = styled('div')`
-  display: grid;
-  grid-template-rows: auto auto auto;
-  gap: ${space(2)};
-  margin-bottom: ${space(2)};
-
-  @media (min-width: ${p => p.theme.breakpoints.small}) {
-    grid-template-rows: auto;
-    grid-template-columns: auto 1fr auto;
-  }
-`;

+ 1 - 1
static/app/views/starfish/utils/routeNames.tsx

@@ -5,5 +5,5 @@ export const ROUTE_NAMES = {
   database: t('Database'),
   'app-startup': t('App Starts'),
   pageload: t('Screen Loads'),
-  responsiveness: t('Responsiveness'),
+  mobileUI: t('Mobile UI'),
 };