Browse Source

feat(Dashboards): use metrics data switcher and alert in dashboards (#37486)

Uses the metrics data switcher alert banner from performance in dashboards
edwardgou-sentry 2 years ago
parent
commit
28ceba9e39

+ 44 - 9
static/app/views/dashboardsV2/detail.tsx

@@ -33,12 +33,25 @@ import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
 import {t} from 'sentry/locale';
 import {PageContent} from 'sentry/styles/organization';
 import space from 'sentry/styles/space';
-import {Organization} from 'sentry/types';
+import {Organization, Project} from 'sentry/types';
 import {defined} from 'sentry/utils';
 import {trackAnalyticsEvent} from 'sentry/utils/analytics';
 import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
 import withApi from 'sentry/utils/withApi';
 import withOrganization from 'sentry/utils/withOrganization';
+import withProjects from 'sentry/utils/withProjects';
+import {
+  cloneDashboard,
+  getCurrentPageFilters,
+  hasSavedPageFilters,
+  hasUnsavedFilterChanges,
+  isWidgetUsingTransactionName,
+  resetPageFilters,
+} from 'sentry/views/dashboardsV2/utils';
+import {MetricsDataSwitcherAlert} from 'sentry/views/performance/landing/metricsDataSwitcherAlert';
+
+import {generatePerformanceEventView} from '../performance/data';
+import {MetricsDataSwitcher} from '../performance/landing/metricsDataSwitcher';
 
 import {
   WidgetViewerContext,
@@ -64,13 +77,6 @@ import {
   Widget,
   WidgetType,
 } from './types';
-import {
-  cloneDashboard,
-  getCurrentPageFilters,
-  hasSavedPageFilters,
-  hasUnsavedFilterChanges,
-  resetPageFilters,
-} from './utils';
 
 const UNSAVED_MESSAGE = t('You have unsaved changes, are you sure you want to leave?');
 
@@ -93,6 +99,7 @@ type Props = RouteComponentProps<RouteParams, {}> & {
   dashboards: DashboardListItem[];
   initialState: DashboardState;
   organization: Organization;
+  projects: Project[];
   route: PlainRoute;
   newWidget?: Widget;
   onDashboardUpdate?: (updatedDashboard: DashboardDetails) => void;
@@ -727,6 +734,7 @@ class DashboardDetail extends Component<Props, State> {
       newWidget,
       onSetNewWidget,
       onDashboardUpdate,
+      projects,
     } = this.props;
     const {modifiedDashboard, dashboardState, widgetLimitReached, seriesData, setData} =
       this.state;
@@ -740,6 +748,12 @@ class DashboardDetail extends Component<Props, State> {
       dashboardState !== DashboardState.CREATE &&
       hasUnsavedFilterChanges(dashboard, location, filters);
 
+    const eventView = generatePerformanceEventView(location, projects);
+
+    const isDashboardUsingTransaction = dashboard.widgets.some(
+      isWidgetUsingTransactionName
+    );
+
     return (
       <SentryDocumentTitle title={dashboard.title} orgSlug={organization.slug}>
         <PageFiltersContainer
@@ -796,6 +810,27 @@ class DashboardDetail extends Component<Props, State> {
               </Layout.Header>
               <Layout.Body>
                 <Layout.Main fullWidth>
+                  {(organization.features.includes('dashboards-mep') ||
+                    organization.features.includes('mep-rollout-flag')) &&
+                  isDashboardUsingTransaction ? (
+                    <MetricsDataSwitcher
+                      organization={organization}
+                      eventView={eventView}
+                      location={location}
+                      hideLoadingIndicator
+                    >
+                      {metricsDataSide => (
+                        <MetricsDataSwitcherAlert
+                          organization={organization}
+                          eventView={eventView}
+                          projects={projects}
+                          location={location}
+                          router={router}
+                          {...metricsDataSide}
+                        />
+                      )}
+                    </MetricsDataSwitcher>
+                  ) : null}
                   <FiltersBar
                     hasUnsavedChanges={disableDashboardModifications}
                     isEditingDashboard={
@@ -901,4 +936,4 @@ const StyledPageFilterBar = styled(PageFilterBar)`
   margin-bottom: ${space(2)};
 `;
 
-export default withApi(withOrganization(DashboardDetail));
+export default withProjects(withApi(withOrganization(DashboardDetail)));

+ 18 - 0
static/app/views/dashboardsV2/utils.tsx

@@ -393,6 +393,24 @@ export function getCustomMeasurementQueryParams() {
   };
 }
 
+export function isWidgetUsingTransactionName(widget: Widget) {
+  return (
+    widget.widgetType === WidgetType.DISCOVER &&
+    widget.queries.some(({aggregates, columns, fields}) => {
+      const aggregateArgs = aggregates.reduce((acc: string[], aggregate) => {
+        const aggregateArg = getAggregateArg(aggregate);
+        if (aggregateArg) {
+          acc.push(aggregateArg);
+        }
+        return acc;
+      }, []);
+      return [...aggregateArgs, ...columns, ...(fields ?? [])].some(
+        field => field === 'transaction'
+      );
+    })
+  );
+}
+
 export function hasSavedPageFilters(dashboard: DashboardDetails) {
   return !(
     isEmpty(dashboard.projects) &&

+ 2 - 1
static/app/views/performance/landing/metricsDataSwitcher.tsx

@@ -29,6 +29,7 @@ interface MetricDataSwitchProps {
   eventView: EventView;
   location: Location;
   organization: Organization;
+  hideLoadingIndicator?: boolean;
 }
 
 /**
@@ -60,7 +61,7 @@ export function MetricsDataSwitcher(props: MetricDataSwitchProps) {
     <Fragment>
       <MetricsCompatibilityQuery eventView={_eventView} {...baseDiscoverProps}>
         {data => {
-          if (data.isLoading) {
+          if (data.isLoading && !props.hideLoadingIndicator) {
             return (
               <Fragment>
                 <LoadingContainer>

+ 1 - 1
tests/js/spec/views/dashboardsV2/detail.spec.jsx

@@ -81,7 +81,7 @@ describe('Dashboards > Detail', function () {
             organization={initialData.organization}
             params={{orgId: 'org-slug', dashboardId: 'default-overview'}}
             router={initialData.router}
-            location={location}
+            location={initialData.router.location}
           />
         </OrganizationContext.Provider>,
         initialData.routerContext