Browse Source

ref(ui): Remove legacy release details codepaths (#29150)

Matej Minar 3 years ago
parent
commit
e0ab3fad53

+ 71 - 0
static/app/utils/sessions.tsx

@@ -11,7 +11,9 @@ import {
 import {SessionApiResponse, SessionField, SessionStatus} from 'app/types';
 import {SeriesDataUnit} from 'app/types/echarts';
 import {defined, percent} from 'app/utils';
+import {Theme} from 'app/utils/theme';
 import {getCrashFreePercent, getSessionStatusPercent} from 'app/views/releases/utils';
+import {sessionTerm} from 'app/views/releases/utils/sessionTerm';
 
 export function getCount(groups: SessionApiResponse['groups'] = [], field: SessionField) {
   return groups.reduce((acc, group) => acc + group.totals[field], 0);
@@ -173,6 +175,75 @@ export function getAdoptionSeries(
   });
 }
 
+export function getCountSeries(
+  field: SessionField,
+  group?: SessionApiResponse['groups'][0],
+  intervals: SessionApiResponse['intervals'] = []
+): SeriesDataUnit[] {
+  return intervals.map((interval, index) => ({
+    name: interval,
+    value: group?.series[field][index] ?? 0,
+  }));
+}
+
+export function initSessionsChart(theme: Theme) {
+  const colors = theme.charts.getColorPalette(14);
+  return {
+    [SessionStatus.HEALTHY]: {
+      seriesName: sessionTerm.healthy,
+      data: [],
+      color: theme.green300,
+      areaStyle: {
+        color: theme.green300,
+        opacity: 1,
+      },
+      lineStyle: {
+        opacity: 0,
+        width: 0.4,
+      },
+    },
+    [SessionStatus.ERRORED]: {
+      seriesName: sessionTerm.errored,
+      data: [],
+      color: colors[12],
+      areaStyle: {
+        color: colors[12],
+        opacity: 1,
+      },
+      lineStyle: {
+        opacity: 0,
+        width: 0.4,
+      },
+    },
+    [SessionStatus.ABNORMAL]: {
+      seriesName: sessionTerm.abnormal,
+      data: [],
+      color: colors[15],
+      areaStyle: {
+        color: colors[15],
+        opacity: 1,
+      },
+      lineStyle: {
+        opacity: 0,
+        width: 0.4,
+      },
+    },
+    [SessionStatus.CRASHED]: {
+      seriesName: sessionTerm.crashed,
+      data: [],
+      color: theme.red300,
+      areaStyle: {
+        color: theme.red300,
+        opacity: 1,
+      },
+      lineStyle: {
+        opacity: 0,
+        width: 0.4,
+      },
+    },
+  };
+}
+
 type GetSessionsIntervalOptions = {
   highFidelity?: boolean;
 };

+ 2 - 2
static/app/views/dashboardsV2/widget/metricWidget/statsRequest.tsx

@@ -10,8 +10,8 @@ import {URL_PARAM} from 'app/constants/globalSelectionHeader';
 import {t} from 'app/locale';
 import {GlobalSelection, Organization, Project, SessionApiResponse} from 'app/types';
 import {Series} from 'app/types/echarts';
+import {getSessionsInterval} from 'app/utils/sessions';
 import {MutableSearch} from 'app/utils/tokenizeSearch';
-import {getInterval} from 'app/views/releases/detail/overview/chart/utils';
 import {roundDuration} from 'app/views/releases/utils';
 
 import {MetricQuery} from './types';
@@ -89,7 +89,7 @@ function StatsRequest({
     const promises = filteredGroupings.map(({metricMeta, aggregation, groupBy}) => {
       const query: RequestQuery = {
         field: `${aggregation}(${metricMeta.name})`,
-        interval: getInterval(datetime),
+        interval: getSessionsInterval(datetime),
         ...requestExtraParams,
       };
 

+ 1 - 2
static/app/views/performance/transactionSummary/transactionOverview/charts.tsx

@@ -18,7 +18,6 @@ import EventView from 'app/utils/discover/eventView';
 import {removeHistogramQueryStrings} from 'app/utils/performance/histogram';
 import {decodeScalar} from 'app/utils/queryString';
 import {TransactionsListOption} from 'app/views/releases/detail/overview';
-import {YAxis} from 'app/views/releases/detail/overview/chart/releaseChartControls';
 
 import {TrendColumnField, TrendFunctionField} from '../../trends/types';
 import {
@@ -148,7 +147,7 @@ class TransactionSummaryCharts extends Component<Props> {
     }
 
     const releaseQueryExtra = {
-      yAxis: display === DisplayModes.VITALS ? YAxis.COUNT_VITAL : YAxis.COUNT_DURATION,
+      yAxis: display === DisplayModes.VITALS ? 'countVital' : 'countDuration',
       showTransactions:
         display === DisplayModes.VITALS
           ? TransactionsListOption.SLOW_LCP

+ 1 - 2
static/app/views/performance/trends/chart.tsx

@@ -16,7 +16,6 @@ import EventView from 'app/utils/discover/eventView';
 import getDynamicText from 'app/utils/getDynamicText';
 import {decodeList} from 'app/utils/queryString';
 import {Theme} from 'app/utils/theme';
-import {YAxis} from 'app/views/releases/detail/overview/chart/releaseChartControls';
 
 import {NormalizedTrendsTransaction, TrendChangeType, TrendsStats} from './types';
 import {
@@ -315,7 +314,7 @@ function Chart({
 
   const queryExtra = {
     showTransactions: trendChangeType,
-    yAxis: YAxis.COUNT_DURATION,
+    yAxis: 'countDuration',
   };
 
   const chartOptions = {

+ 46 - 17
static/app/views/projectDetail/charts/sessionsRequest.tsx

@@ -8,16 +8,18 @@ import {Client} from 'app/api';
 import {getSeriesApiInterval} from 'app/components/charts/utils';
 import {getParams} from 'app/components/organizations/globalSelectionHeader/getParams';
 import {t} from 'app/locale';
-import {GlobalSelection, Organization, SessionApiResponse} from 'app/types';
+import {
+  GlobalSelection,
+  Organization,
+  SessionApiResponse,
+  SessionField,
+  SessionStatus,
+} from 'app/types';
 import {Series} from 'app/types/echarts';
 import {percent} from 'app/utils';
 import {getPeriod} from 'app/utils/getPeriod';
+import {getCount, getCountSeries, initSessionsChart} from 'app/utils/sessions';
 import {Theme} from 'app/utils/theme';
-import {
-  fillChartDataFromSessionsResponse,
-  getTotalsFromSessionsResponse,
-  initSessionsBreakdownChartData,
-} from 'app/views/releases/detail/overview/chart/utils';
 import {getCrashFreePercent} from 'app/views/releases/utils';
 
 import {DisplayModes} from '../projectCharts';
@@ -273,21 +275,48 @@ class SessionsRequest extends React.Component<Props, State> {
 
   transformSessionCountData(responseData: SessionApiResponse) {
     const {theme} = this.props;
+    const sessionsChart = initSessionsChart(theme);
+    const {intervals, groups} = responseData;
 
-    const totalSessions = getTotalsFromSessionsResponse({
-      response: responseData,
-      field: 'sum(session)',
-    });
+    const totalSessions = getCount(responseData.groups, SessionField.SESSIONS);
 
-    const chartData = fillChartDataFromSessionsResponse({
-      response: responseData,
-      field: 'sum(session)',
-      groupBy: 'session.status',
-      chartData: initSessionsBreakdownChartData(theme),
-    });
+    const chartData = [
+      {
+        ...sessionsChart[SessionStatus.HEALTHY],
+        data: getCountSeries(
+          SessionField.SESSIONS,
+          groups.find(g => g.by['session.status'] === SessionStatus.HEALTHY),
+          intervals
+        ),
+      },
+      {
+        ...sessionsChart[SessionStatus.ERRORED],
+        data: getCountSeries(
+          SessionField.SESSIONS,
+          groups.find(g => g.by['session.status'] === SessionStatus.ERRORED),
+          intervals
+        ),
+      },
+      {
+        ...sessionsChart[SessionStatus.ABNORMAL],
+        data: getCountSeries(
+          SessionField.SESSIONS,
+          groups.find(g => g.by['session.status'] === SessionStatus.ABNORMAL),
+          intervals
+        ),
+      },
+      {
+        ...sessionsChart[SessionStatus.CRASHED],
+        data: getCountSeries(
+          SessionField.SESSIONS,
+          groups.find(g => g.by['session.status'] === SessionStatus.CRASHED),
+          intervals
+        ),
+      },
+    ];
 
     return {
-      timeseriesData: Object.values(chartData),
+      timeseriesData: chartData,
       previousTimeseriesData: null,
       totalSessions,
     };

+ 7 - 74
static/app/views/releases/detail/index.tsx

@@ -2,7 +2,6 @@ import {createContext} from 'react';
 import {RouteComponentProps} from 'react-router';
 import styled from '@emotion/styled';
 import pick from 'lodash/pick';
-import moment from 'moment';
 
 import Alert from 'app/components/alert';
 import AsyncComponent from 'app/components/asyncComponent';
@@ -11,7 +10,6 @@ import NoProjectMessage from 'app/components/noProjectMessage';
 import GlobalSelectionHeader from 'app/components/organizations/globalSelectionHeader';
 import {getParams} from 'app/components/organizations/globalSelectionHeader/getParams';
 import PickProjectToContinue from 'app/components/pickProjectToContinue';
-import {DEFAULT_STATS_PERIOD} from 'app/constants';
 import {URL_PARAM} from 'app/constants/globalSelectionHeader';
 import {IconInfo, IconWarning} from 'app/icons';
 import {t} from 'app/locale';
@@ -34,25 +32,16 @@ import withGlobalSelection from 'app/utils/withGlobalSelection';
 import withOrganization from 'app/utils/withOrganization';
 import AsyncView from 'app/views/asyncView';
 
-import {DisplayOption} from '../list/utils';
 import {getReleaseBounds, ReleaseBounds} from '../utils';
-import ReleaseHealthRequest, {
-  ReleaseHealthRequestRenderProps,
-} from '../utils/releaseHealthRequest';
 
 import ReleaseHeader from './releaseHeader';
 
-const DEFAULT_FRESH_RELEASE_STATS_PERIOD = '24h';
-
 type ReleaseContextType = {
   release: ReleaseWithHealth;
   project: Required<ReleaseProject>;
   deploys: Deploy[];
   releaseMeta: ReleaseMeta;
   refetchData: () => void;
-  defaultStatsPeriod: string;
-  getHealthData: ReleaseHealthRequestRenderProps['getHealthData'];
-  isHealthLoading: ReleaseHealthRequestRenderProps['isHealthLoading'];
   hasHealthData: boolean;
   releaseBounds: ReleaseBounds;
 };
@@ -67,9 +56,6 @@ type Props = RouteComponentProps<RouteParams, {}> & {
   organization: Organization;
   selection: GlobalSelection;
   releaseMeta: ReleaseMeta;
-  defaultStatsPeriod: string;
-  getHealthData: ReleaseHealthRequestRenderProps['getHealthData'];
-  isHealthLoading: ReleaseHealthRequestRenderProps['isHealthLoading'];
 };
 
 type State = {
@@ -105,7 +91,7 @@ class ReleasesDetail extends AsyncView<Props, State> {
   }
 
   getEndpoints(): ReturnType<AsyncComponent['getEndpoints']> {
-    const {organization, location, params, releaseMeta, defaultStatsPeriod} = this.props;
+    const {organization, location, params, releaseMeta} = this.props;
 
     const basePath = `/organizations/${organization.slug}/releases/${encodeURIComponent(
       params.release
@@ -118,9 +104,7 @@ class ReleasesDetail extends AsyncView<Props, State> {
         {
           query: {
             adoptionStages: 1,
-            ...getParams(pick(location.query, [...Object.values(URL_PARAM)]), {
-              defaultStatsPeriod,
-            }),
+            ...getParams(pick(location.query, [...Object.values(URL_PARAM)])),
           },
         },
       ],
@@ -176,15 +160,7 @@ class ReleasesDetail extends AsyncView<Props, State> {
   }
 
   renderBody() {
-    const {
-      organization,
-      location,
-      selection,
-      releaseMeta,
-      defaultStatsPeriod,
-      getHealthData,
-      isHealthLoading,
-    } = this.props;
+    const {organization, location, selection, releaseMeta} = this.props;
     const {release, deploys, sessions, reloading} = this.state;
     const project = release?.projects.find(p => p.id === selection.projects[0]);
     const releaseBounds = getReleaseBounds(release);
@@ -215,9 +191,6 @@ class ReleasesDetail extends AsyncView<Props, State> {
               deploys,
               releaseMeta,
               refetchData: this.fetchData,
-              defaultStatsPeriod,
-              getHealthData,
-              isHealthLoading,
               hasHealthData: getCount(sessions?.groups, SessionField.SESSIONS) > 0,
               releaseBounds,
             }}
@@ -231,7 +204,7 @@ class ReleasesDetail extends AsyncView<Props, State> {
 }
 
 class ReleasesDetailContainer extends AsyncComponent<
-  Omit<Props, 'releaseMeta' | 'getHealthData' | 'isHealthLoading'>,
+  Omit<Props, 'releaseMeta'>,
   {releaseMeta: ReleaseMeta | null} & AsyncComponent['state']
 > {
   shouldReload = true;
@@ -249,10 +222,6 @@ class ReleasesDetailContainer extends AsyncComponent<
     ];
   }
 
-  get hasReleaseComparison() {
-    return this.props.organization.features.includes('release-comparison');
-  }
-
   componentDidMount() {
     this.removeGlobalDateTimeFromUrl();
   }
@@ -266,10 +235,6 @@ class ReleasesDetailContainer extends AsyncComponent<
     const {router, location} = this.props;
     const {start, end, statsPeriod, utc, ...restQuery} = location.query;
 
-    if (!this.hasReleaseComparison) {
-      return;
-    }
-
     if (start || end || statsPeriod || utc) {
       router.replace({
         ...location,
@@ -318,7 +283,7 @@ class ReleasesDetailContainer extends AsyncComponent<
   }
 
   renderBody() {
-    const {organization, params, router, location, selection} = this.props;
+    const {organization, params, router} = this.props;
     const {releaseMeta} = this.state;
 
     if (!releaseMeta) {
@@ -326,12 +291,6 @@ class ReleasesDetailContainer extends AsyncComponent<
     }
 
     const {projects} = releaseMeta;
-    const isFreshRelease = moment(releaseMeta.released).isAfter(
-      moment().subtract(24, 'hours')
-    );
-    const defaultStatsPeriod = isFreshRelease
-      ? DEFAULT_FRESH_RELEASE_STATS_PERIOD
-      : DEFAULT_STATS_PERIOD;
 
     if (this.isProjectMissingInUrl()) {
       return (
@@ -362,35 +321,9 @@ class ReleasesDetailContainer extends AsyncComponent<
         disableMultipleProjectSelection
         showProjectSettingsLink
         projectsFooterMessage={this.renderProjectsFooterMessage()}
-        defaultSelection={{
-          datetime: {
-            start: null,
-            end: null,
-            utc: false,
-            period: defaultStatsPeriod,
-          },
-        }}
-        showDateSelector={!this.hasReleaseComparison}
+        showDateSelector={false}
       >
-        <ReleaseHealthRequest
-          releases={[params.release]}
-          organization={organization}
-          selection={selection}
-          location={location}
-          display={[DisplayOption.SESSIONS, DisplayOption.USERS]}
-          defaultStatsPeriod={defaultStatsPeriod}
-          disable={this.hasReleaseComparison}
-        >
-          {({isHealthLoading, getHealthData}) => (
-            <ReleasesDetail
-              {...this.props}
-              releaseMeta={releaseMeta}
-              defaultStatsPeriod={defaultStatsPeriod}
-              getHealthData={getHealthData}
-              isHealthLoading={isHealthLoading}
-            />
-          )}
-        </ReleaseHealthRequest>
+        <ReleasesDetail {...this.props} releaseMeta={releaseMeta} />
       </GlobalSelectionHeader>
     );
   }

+ 0 - 314
static/app/views/releases/detail/overview/chart/healthChart.tsx

@@ -1,314 +0,0 @@
-import * as React from 'react';
-import {browserHistory} from 'react-router';
-import {withTheme} from '@emotion/react';
-import {Location} from 'history';
-import isEqual from 'lodash/isEqual';
-
-import AreaChart from 'app/components/charts/areaChart';
-import {ZoomRenderProps} from 'app/components/charts/chartZoom';
-import LineChart from 'app/components/charts/lineChart';
-import StackedAreaChart from 'app/components/charts/stackedAreaChart';
-import {HeaderTitleLegend} from 'app/components/charts/styles';
-import {getSeriesSelection} from 'app/components/charts/utils';
-import {parseStatsPeriod} from 'app/components/organizations/timeRangeSelector/utils';
-import QuestionTooltip from 'app/components/questionTooltip';
-import {PlatformKey} from 'app/data/platformCategories';
-import {Series} from 'app/types/echarts';
-import {defined} from 'app/utils';
-import {getUtcDateString} from 'app/utils/dates';
-import {axisDuration} from 'app/utils/discover/charts';
-import {getExactDuration} from 'app/utils/formatters';
-import {decodeScalar} from 'app/utils/queryString';
-import {Theme} from 'app/utils/theme';
-import {displayCrashFreePercent} from 'app/views/releases/utils';
-
-import {
-  getSessionTermDescription,
-  SessionTerm,
-  sessionTerm,
-} from '../../../utils/sessionTerm';
-
-import {YAxis} from './releaseChartControls';
-import {isOtherSeries, sortSessionSeries} from './utils';
-
-type Props = {
-  theme: Theme;
-  reloading: boolean;
-  timeseriesData: Series[];
-  zoomRenderProps: ZoomRenderProps;
-  yAxis: YAxis;
-  location: Location;
-  platform: PlatformKey;
-  shouldRecalculateVisibleSeries: boolean;
-  onVisibleSeriesRecalculated: () => void;
-  title: string;
-  help?: string;
-};
-
-class HealthChart extends React.Component<Props> {
-  componentDidMount() {
-    if (this.shouldUnselectHealthySeries()) {
-      this.props.onVisibleSeriesRecalculated();
-      this.handleLegendSelectChanged({selected: {Healthy: false}});
-    }
-  }
-
-  shouldComponentUpdate(nextProps: Props) {
-    if (this.props.title !== nextProps.title) {
-      return true;
-    }
-
-    if (nextProps.reloading || !nextProps.timeseriesData) {
-      return false;
-    }
-
-    if (
-      this.props.location.query.unselectedSeries !==
-      nextProps.location.query.unselectedSeries
-    ) {
-      return true;
-    }
-
-    if (isEqual(this.props.timeseriesData, nextProps.timeseriesData)) {
-      return false;
-    }
-
-    return true;
-  }
-
-  shouldUnselectHealthySeries(): boolean {
-    const {timeseriesData, location, shouldRecalculateVisibleSeries} = this.props;
-
-    const otherAreasThanHealthyArePositive = timeseriesData
-      .filter(
-        s =>
-          ![
-            sessionTerm.healthy,
-            sessionTerm.otherHealthy,
-            sessionTerm.otherErrored,
-            sessionTerm.otherCrashed,
-            sessionTerm.otherAbnormal,
-          ].includes(s.seriesName)
-      )
-      .some(s => s.data.some(d => d.value > 0));
-    const alreadySomethingUnselected = !!decodeScalar(location.query.unselectedSeries);
-
-    return (
-      shouldRecalculateVisibleSeries &&
-      otherAreasThanHealthyArePositive &&
-      !alreadySomethingUnselected
-    );
-  }
-
-  handleLegendSelectChanged = legendChange => {
-    const {location} = this.props;
-    const {selected} = legendChange;
-
-    const to = {
-      ...location,
-      query: {
-        ...location.query,
-        unselectedSeries: Object.keys(selected).filter(key => !selected[key]),
-      },
-    };
-
-    browserHistory.replace(to);
-  };
-
-  formatTooltipValue = (value: string | number | null) => {
-    const {yAxis} = this.props;
-    switch (yAxis) {
-      case YAxis.SESSION_DURATION:
-        return typeof value === 'number' ? getExactDuration(value, true) : '\u2015';
-      case YAxis.CRASH_FREE:
-        return defined(value) ? `${value}%` : '\u2015';
-      case YAxis.SESSIONS:
-      case YAxis.USERS:
-      default:
-        return typeof value === 'number' ? value.toLocaleString() : value ?? '';
-    }
-  };
-
-  configureYAxis() {
-    const {theme, yAxis} = this.props;
-    switch (yAxis) {
-      case YAxis.CRASH_FREE:
-        return {
-          max: 100,
-          scale: true,
-          axisLabel: {
-            formatter: (value: number) => displayCrashFreePercent(value),
-            color: theme.chartLabel,
-          },
-        };
-      case YAxis.SESSION_DURATION:
-        return {
-          scale: true,
-          axisLabel: {
-            formatter: value => axisDuration(value * 1000),
-            color: theme.chartLabel,
-          },
-        };
-      case YAxis.SESSIONS:
-      case YAxis.USERS:
-      default:
-        return undefined;
-    }
-  }
-
-  configureXAxis() {
-    const {timeseriesData, zoomRenderProps} = this.props;
-
-    if (timeseriesData.every(s => s.data.length === 1)) {
-      if (zoomRenderProps.period) {
-        const {start, end} = parseStatsPeriod(zoomRenderProps.period, null);
-
-        return {min: start, max: end};
-      }
-
-      return {
-        min: getUtcDateString(zoomRenderProps.start),
-        max: getUtcDateString(zoomRenderProps.end),
-      };
-    }
-
-    return undefined;
-  }
-
-  getChart():
-    | React.ComponentType<StackedAreaChart['props']>
-    | React.ComponentType<AreaChart['props']>
-    | React.ComponentType<LineChart['props']> {
-    const {yAxis} = this.props;
-    switch (yAxis) {
-      case YAxis.SESSION_DURATION:
-        return AreaChart;
-      case YAxis.SESSIONS:
-      case YAxis.USERS:
-        return StackedAreaChart;
-      case YAxis.CRASH_FREE:
-      default:
-        return LineChart;
-    }
-  }
-
-  getLegendTooltipDescription(serieName: string) {
-    const {platform} = this.props;
-
-    switch (serieName) {
-      case sessionTerm.crashed:
-        return getSessionTermDescription(SessionTerm.CRASHED, platform);
-      case sessionTerm.abnormal:
-        return getSessionTermDescription(SessionTerm.ABNORMAL, platform);
-      case sessionTerm.errored:
-        return getSessionTermDescription(SessionTerm.ERRORED, platform);
-      case sessionTerm.healthy:
-        return getSessionTermDescription(SessionTerm.HEALTHY, platform);
-      case sessionTerm['crash-free-users']:
-        return getSessionTermDescription(SessionTerm.CRASH_FREE_USERS, platform);
-      case sessionTerm['crash-free-sessions']:
-        return getSessionTermDescription(SessionTerm.CRASH_FREE_SESSIONS, platform);
-      default:
-        return '';
-    }
-  }
-
-  getLegendSeries() {
-    const {timeseriesData} = this.props;
-    return (
-      timeseriesData
-        .filter(d => !isOtherSeries(d))
-        // we don't want Other Healthy, Other Crashed, etc. to show up in the legend
-        .sort(sortSessionSeries)
-        .map(d => d.seriesName)
-    );
-  }
-
-  getLegendSelectedSeries() {
-    const {location, yAxis} = this.props;
-
-    const selection = getSeriesSelection(location) ?? {};
-    // otherReleases toggles all "other" series (other healthy, other crashed, etc.) at once
-    const otherReleasesVisible = !(selection[sessionTerm.otherReleases] === false);
-
-    if (yAxis === YAxis.SESSIONS || yAxis === YAxis.USERS) {
-      selection[sessionTerm.otherErrored] =
-        !selection?.hasOwnProperty(sessionTerm.errored) && otherReleasesVisible;
-      selection[sessionTerm.otherCrashed] =
-        !selection?.hasOwnProperty(sessionTerm.crashed) && otherReleasesVisible;
-      selection[sessionTerm.otherAbnormal] =
-        !selection?.hasOwnProperty(sessionTerm.abnormal) && otherReleasesVisible;
-      selection[sessionTerm.otherHealthy] =
-        !selection?.hasOwnProperty(sessionTerm.healthy) && otherReleasesVisible;
-    }
-
-    if (yAxis === YAxis.CRASH_FREE) {
-      selection[sessionTerm.otherCrashFreeSessions] =
-        !selection?.hasOwnProperty(sessionTerm['crash-free-sessions']) &&
-        otherReleasesVisible;
-      selection[sessionTerm.otherCrashFreeUsers] =
-        !selection?.hasOwnProperty(sessionTerm['crash-free-users']) &&
-        otherReleasesVisible;
-    }
-
-    return selection;
-  }
-
-  render() {
-    const {timeseriesData, zoomRenderProps, title, help} = this.props;
-
-    const Chart = this.getChart();
-
-    const legend = {
-      right: 10,
-      top: 0,
-      data: this.getLegendSeries(),
-      selected: this.getLegendSelectedSeries(),
-      tooltip: {
-        show: true,
-        // TODO(ts) tooltip.formatter has incorrect types in echarts 4
-        formatter: (params: any): string => {
-          const seriesNameDesc = this.getLegendTooltipDescription(params.name ?? '');
-
-          if (!seriesNameDesc) {
-            return '';
-          }
-
-          return ['<div class="tooltip-description">', seriesNameDesc, '</div>'].join('');
-        },
-      },
-    };
-
-    return (
-      <React.Fragment>
-        <HeaderTitleLegend>
-          {title}
-          {help && <QuestionTooltip size="sm" position="top" title={help} />}
-        </HeaderTitleLegend>
-
-        <Chart
-          legend={legend}
-          {...zoomRenderProps}
-          series={timeseriesData}
-          isGroupedByDate
-          seriesOptions={{
-            showSymbol: false,
-          }}
-          grid={{
-            left: '10px',
-            right: '10px',
-            top: '40px',
-            bottom: '0px',
-          }}
-          yAxis={this.configureYAxis()}
-          xAxis={this.configureXAxis()}
-          tooltip={{valueFormatter: this.formatTooltipValue}}
-          onLegendSelectChanged={this.handleLegendSelectChanged}
-          transformSinglePointToBar
-        />
-      </React.Fragment>
-    );
-  }
-}
-
-export default withTheme(HealthChart);

+ 0 - 108
static/app/views/releases/detail/overview/chart/healthChartContainer.tsx

@@ -1,108 +0,0 @@
-import {Component} from 'react';
-import {InjectedRouter} from 'react-router';
-
-import ChartZoom from 'app/components/charts/chartZoom';
-import ErrorPanel from 'app/components/charts/errorPanel';
-import TransitionChart from 'app/components/charts/transitionChart';
-import TransparentLoadingMask from 'app/components/charts/transparentLoadingMask';
-import {PlatformKey} from 'app/data/platformCategories';
-import {IconWarning} from 'app/icons';
-import {GlobalSelection} from 'app/types';
-import {sessionTerm} from 'app/views/releases/utils/sessionTerm';
-
-import {ReleaseStatsRequestRenderProps} from '../releaseStatsRequest';
-
-import HealthChart from './healthChart';
-import {YAxis} from './releaseChartControls';
-import {sortSessionSeries} from './utils';
-
-type Props = Omit<
-  ReleaseStatsRequestRenderProps,
-  'crashFreeTimeBreakdown' | 'chartSummary'
-> & {
-  selection: GlobalSelection;
-  yAxis: YAxis;
-  router: InjectedRouter;
-  platform: PlatformKey;
-  title: string;
-  help?: string;
-};
-
-type State = {
-  shouldRecalculateVisibleSeries: boolean;
-};
-
-class ReleaseChartContainer extends Component<Props, State> {
-  state: State = {
-    shouldRecalculateVisibleSeries: true,
-  };
-
-  handleVisibleSeriesRecalculated = () => {
-    this.setState({shouldRecalculateVisibleSeries: false});
-  };
-
-  render() {
-    const {
-      loading,
-      errored,
-      reloading,
-      chartData,
-      selection,
-      yAxis,
-      router,
-      platform,
-      title,
-      help,
-    } = this.props;
-    const {shouldRecalculateVisibleSeries} = this.state;
-    const {datetime} = selection;
-    const {utc, period, start, end} = datetime;
-
-    const timeseriesData = chartData.filter(({seriesName}) => {
-      // There is no concept of Abnormal sessions in javascript
-      if (
-        (seriesName === sessionTerm.abnormal ||
-          seriesName === sessionTerm.otherAbnormal) &&
-        ['javascript', 'node'].includes(platform)
-      ) {
-        return false;
-      }
-
-      return true;
-    });
-
-    return (
-      <ChartZoom router={router} period={period} utc={utc} start={start} end={end}>
-        {zoomRenderProps => {
-          if (errored) {
-            return (
-              <ErrorPanel>
-                <IconWarning color="gray300" size="lg" />
-              </ErrorPanel>
-            );
-          }
-
-          return (
-            <TransitionChart loading={loading} reloading={reloading}>
-              <TransparentLoadingMask visible={reloading} />
-              <HealthChart
-                timeseriesData={timeseriesData.sort(sortSessionSeries)}
-                zoomRenderProps={zoomRenderProps}
-                reloading={reloading}
-                yAxis={yAxis}
-                location={router.location}
-                shouldRecalculateVisibleSeries={shouldRecalculateVisibleSeries}
-                onVisibleSeriesRecalculated={this.handleVisibleSeriesRecalculated}
-                platform={platform}
-                title={title}
-                help={help}
-              />
-            </TransitionChart>
-          );
-        }}
-      </ChartZoom>
-    );
-  }
-}
-
-export default ReleaseChartContainer;

+ 0 - 345
static/app/views/releases/detail/overview/chart/index.tsx

@@ -1,345 +0,0 @@
-import {Component, Fragment} from 'react';
-import {InjectedRouter} from 'react-router';
-import {withTheme} from '@emotion/react';
-import styled from '@emotion/styled';
-import {Location} from 'history';
-
-import {Client} from 'app/api';
-import GuideAnchor from 'app/components/assistant/guideAnchor';
-import EventsChart from 'app/components/charts/eventsChart';
-import {ChartContainer, HeaderTitleLegend} from 'app/components/charts/styles';
-import {Panel} from 'app/components/panels';
-import QuestionTooltip from 'app/components/questionTooltip';
-import {PlatformKey} from 'app/data/platformCategories';
-import {t} from 'app/locale';
-import {GlobalSelection, Organization, ReleaseMeta} from 'app/types';
-import {Series} from 'app/types/echarts';
-import {trackAnalyticsEvent} from 'app/utils/analytics';
-import {WebVital} from 'app/utils/discover/fields';
-import {decodeScalar} from 'app/utils/queryString';
-import {Theme} from 'app/utils/theme';
-import {getTermHelp, PERFORMANCE_TERM} from 'app/views/performance/data';
-
-import ReleaseStatsRequest from '../releaseStatsRequest';
-
-import HealthChartContainer from './healthChartContainer';
-import ReleaseChartControls, {
-  EventType,
-  PERFORMANCE_AXIS,
-  YAxis,
-} from './releaseChartControls';
-import {getReleaseEventView} from './utils';
-
-type Props = {
-  releaseMeta: ReleaseMeta;
-  selection: GlobalSelection;
-  platform: PlatformKey;
-  yAxis: YAxis;
-  eventType: EventType;
-  vitalType: WebVital;
-  onYAxisChange: (yAxis: YAxis) => void;
-  onEventTypeChange: (eventType: EventType) => void;
-  onVitalTypeChange: (vitalType: WebVital) => void;
-  router: InjectedRouter;
-  organization: Organization;
-  hasHealthData: boolean;
-  location: Location;
-  api: Client;
-  version: string;
-  hasDiscover: boolean;
-  hasPerformance: boolean;
-  theme: Theme;
-  defaultStatsPeriod: string;
-  projectSlug: string;
-};
-
-class ReleaseChartContainer extends Component<Props> {
-  componentDidMount() {
-    const {organization, yAxis, platform} = this.props;
-
-    trackAnalyticsEvent({
-      eventKey: `release_detail.display_chart`,
-      eventName: `Release Detail: Display Chart`,
-      organization_id: parseInt(organization.id, 10),
-      display: yAxis,
-      platform,
-    });
-  }
-  /**
-   * This returns an array with 3 colors, one for each of
-   * 1. This Release
-   * 2. Other Releases
-   * 3. Releases (the markers)
-   */
-  getTransactionsChartColors(): [string, string, string] {
-    const {yAxis, theme} = this.props;
-
-    switch (yAxis) {
-      case YAxis.FAILED_TRANSACTIONS:
-        return [theme.red300, theme.red100, theme.purple300];
-      default:
-        return [theme.purple300, theme.purple100, theme.purple300];
-    }
-  }
-
-  getChartTitle() {
-    const {yAxis, organization} = this.props;
-
-    switch (yAxis) {
-      case YAxis.SESSIONS:
-        return {
-          title: t('Session Count'),
-          help: t('The number of sessions in a given period.'),
-        };
-      case YAxis.USERS:
-        return {
-          title: t('User Count'),
-          help: t('The number of users in a given period.'),
-        };
-      case YAxis.SESSION_DURATION:
-        return {title: t('Session Duration')};
-      case YAxis.CRASH_FREE:
-        return {title: t('Crash Free Rate')};
-      case YAxis.FAILED_TRANSACTIONS:
-        return {
-          title: t('Failure Count'),
-          help: getTermHelp(organization, PERFORMANCE_TERM.FAILURE_RATE),
-        };
-      case YAxis.COUNT_DURATION:
-        return {title: t('Slow Duration Count')};
-      case YAxis.COUNT_VITAL:
-        return {title: t('Slow Vital Count')};
-      case YAxis.EVENTS:
-      default:
-        return {title: t('Event Count')};
-    }
-  }
-
-  cloneSeriesAsZero(series: Series): Series {
-    return {
-      ...series,
-      data: series.data.map(point => ({
-        ...point,
-        value: 0,
-      })),
-    };
-  }
-
-  /**
-   * The top events endpoint used to generate these series is not guaranteed to return a series
-   * for both the current release and the other releases. This happens when there is insufficient
-   * data. In these cases, the endpoint will return a single zerofilled series for the current
-   * release.
-   *
-   * This is problematic as we want to show both series even if one is empty. To deal with this,
-   * we clone the non empty series (to preserve the timestamps) with value 0 (to represent the
-   * lack of data).
-   */
-  seriesTransformer = (series: Series[]): Series[] => {
-    let current: Series | null = null;
-    let others: Series | null = null;
-    const allSeries: Series[] = [];
-    series.forEach(s => {
-      if (s.seriesName === 'current' || s.seriesName === t('This Release')) {
-        current = s;
-      } else if (s.seriesName === 'others' || s.seriesName === t('Other Releases')) {
-        others = s;
-      } else {
-        allSeries.push(s);
-      }
-    });
-
-    if (current !== null && others === null) {
-      others = this.cloneSeriesAsZero(current);
-    } else if (current === null && others !== null) {
-      current = this.cloneSeriesAsZero(others);
-    }
-
-    if (others !== null) {
-      others.seriesName = t('Other Releases');
-      allSeries.unshift(others);
-    }
-
-    if (current !== null) {
-      current.seriesName = t('This Release');
-      allSeries.unshift(current);
-    }
-
-    return allSeries;
-  };
-
-  renderStackedChart() {
-    const {
-      location,
-      router,
-      organization,
-      api,
-      releaseMeta,
-      yAxis,
-      eventType,
-      vitalType,
-      selection,
-      version,
-    } = this.props;
-    const {projects, environments, datetime} = selection;
-    const {start, end, period, utc} = datetime;
-    const eventView = getReleaseEventView(
-      selection,
-      version,
-      yAxis,
-      eventType,
-      vitalType,
-      organization
-    );
-    const apiPayload = eventView.getEventsAPIPayload(location);
-    const colors = this.getTransactionsChartColors();
-    const {title, help} = this.getChartTitle();
-
-    const releaseQueryExtra = {
-      showTransactions: location.query.showTransactions,
-      eventType,
-      vitalType,
-      yAxis,
-    };
-
-    return (
-      <EventsChart
-        router={router}
-        organization={organization}
-        showLegend
-        yAxis={eventView.getYAxis()}
-        query={apiPayload.query}
-        api={api}
-        projects={projects}
-        environments={environments}
-        start={start}
-        end={end}
-        period={period}
-        utc={utc}
-        disablePrevious
-        emphasizeReleases={[releaseMeta.version]}
-        field={eventView.getFields()}
-        topEvents={2}
-        orderby={decodeScalar(apiPayload.sort)}
-        currentSeriesName={t('This Release')}
-        // This seems a little strange but is intentional as EventsChart
-        // uses the previousSeriesName as the secondary series name
-        previousSeriesName={t('Other Releases')}
-        seriesTransformer={this.seriesTransformer}
-        disableableSeries={[t('This Release'), t('Other Releases')]}
-        colors={colors}
-        preserveReleaseQueryParams
-        releaseQueryExtra={releaseQueryExtra}
-        chartHeader={
-          <HeaderTitleLegend>
-            {title}
-            {help && <QuestionTooltip size="sm" position="top" title={help} />}
-          </HeaderTitleLegend>
-        }
-        legendOptions={{right: 10, top: 0}}
-        chartOptions={{grid: {left: '10px', right: '10px', top: '40px', bottom: '0px'}}}
-      />
-    );
-  }
-
-  renderHealthChart(
-    loading: boolean,
-    reloading: boolean,
-    errored: boolean,
-    chartData: Series[]
-  ) {
-    const {selection, yAxis, router, platform} = this.props;
-    const {title, help} = this.getChartTitle();
-
-    return (
-      <HealthChartContainer
-        platform={platform}
-        loading={loading}
-        errored={errored}
-        reloading={reloading}
-        chartData={chartData}
-        selection={selection}
-        yAxis={yAxis}
-        router={router}
-        title={title}
-        help={help}
-      />
-    );
-  }
-
-  render() {
-    const {
-      yAxis,
-      eventType,
-      vitalType,
-      hasDiscover,
-      hasHealthData,
-      hasPerformance,
-      onYAxisChange,
-      onEventTypeChange,
-      onVitalTypeChange,
-      organization,
-      defaultStatsPeriod,
-      api,
-      version,
-      selection,
-      location,
-      projectSlug,
-    } = this.props;
-
-    return (
-      <ReleaseStatsRequest
-        api={api}
-        organization={organization}
-        projectSlug={projectSlug}
-        version={version}
-        selection={selection}
-        location={location}
-        yAxis={yAxis}
-        eventType={eventType}
-        vitalType={vitalType}
-        hasHealthData={hasHealthData}
-        hasDiscover={hasDiscover}
-        hasPerformance={hasPerformance}
-        defaultStatsPeriod={defaultStatsPeriod}
-      >
-        {({loading, reloading, errored, chartData, chartSummary}) => (
-          <Panel>
-            <ChartContainer>
-              {((hasDiscover || hasPerformance) && yAxis === YAxis.EVENTS) ||
-              (hasPerformance && PERFORMANCE_AXIS.includes(yAxis))
-                ? this.renderStackedChart()
-                : this.renderHealthChart(loading, reloading, errored, chartData)}
-            </ChartContainer>
-            <AnchorWrapper>
-              <GuideAnchor target="release_chart" position="bottom" offset="-80px">
-                <Fragment />
-              </GuideAnchor>
-            </AnchorWrapper>
-            <ReleaseChartControls
-              summary={chartSummary}
-              yAxis={yAxis}
-              onYAxisChange={onYAxisChange}
-              eventType={eventType}
-              onEventTypeChange={onEventTypeChange}
-              vitalType={vitalType}
-              onVitalTypeChange={onVitalTypeChange}
-              organization={organization}
-              hasDiscover={hasDiscover}
-              hasHealthData={hasHealthData}
-              hasPerformance={hasPerformance}
-            />
-          </Panel>
-        )}
-      </ReleaseStatsRequest>
-    );
-  }
-}
-
-export default withTheme(ReleaseChartContainer);
-
-const AnchorWrapper = styled('div')`
-  height: 0;
-  width: 0;
-  margin-left: 50%;
-`;

+ 0 - 238
static/app/views/releases/detail/overview/chart/releaseChartControls.tsx

@@ -1,238 +0,0 @@
-import * as React from 'react';
-
-import OptionSelector from 'app/components/charts/optionSelector';
-import {
-  ChartControls,
-  InlineContainer,
-  SectionHeading,
-  SectionValue,
-} from 'app/components/charts/styles';
-import QuestionTooltip from 'app/components/questionTooltip';
-import NOT_AVAILABLE_MESSAGES from 'app/constants/notAvailableMessages';
-import {t} from 'app/locale';
-import {Organization, SelectValue} from 'app/types';
-import {WebVital} from 'app/utils/discover/fields';
-import {WEB_VITAL_DETAILS} from 'app/utils/performance/vitals/constants';
-
-export enum YAxis {
-  SESSIONS = 'sessions',
-  USERS = 'users',
-  CRASH_FREE = 'crashFree',
-  SESSION_DURATION = 'sessionDuration',
-  EVENTS = 'events',
-  FAILED_TRANSACTIONS = 'failedTransactions',
-  COUNT_DURATION = 'countDuration',
-  COUNT_VITAL = 'countVital',
-}
-
-export enum EventType {
-  ALL = 'all',
-  CSP = 'csp',
-  DEFAULT = 'default',
-  ERROR = 'error',
-  TRANSACTION = 'transaction',
-}
-
-export const PERFORMANCE_AXIS = [
-  YAxis.FAILED_TRANSACTIONS,
-  YAxis.COUNT_DURATION,
-  YAxis.COUNT_VITAL,
-];
-
-type Props = {
-  summary: React.ReactNode;
-  yAxis: YAxis;
-  onYAxisChange: (value: YAxis) => void;
-  eventType: EventType;
-  onEventTypeChange: (value: EventType) => void;
-  vitalType: WebVital;
-  onVitalTypeChange: (value: WebVital) => void;
-  organization: Organization;
-  hasHealthData: boolean;
-  hasDiscover: boolean;
-  hasPerformance: boolean;
-};
-
-const ReleaseChartControls = ({
-  summary,
-  yAxis,
-  onYAxisChange,
-  organization,
-  hasHealthData,
-  hasDiscover,
-  hasPerformance,
-  eventType = EventType.ALL,
-  onEventTypeChange,
-  vitalType = WebVital.LCP,
-  onVitalTypeChange,
-}: Props) => {
-  const noHealthDataTooltip = !hasHealthData
-    ? NOT_AVAILABLE_MESSAGES.releaseHealth
-    : undefined;
-  const noDiscoverTooltip = !hasDiscover ? NOT_AVAILABLE_MESSAGES.discover : undefined;
-  const noPerformanceTooltip = !hasPerformance
-    ? NOT_AVAILABLE_MESSAGES.performance
-    : undefined;
-  const yAxisOptions: SelectValue<YAxis>[] = [
-    {
-      value: YAxis.SESSIONS,
-      label: t('Session Count'),
-      disabled: !hasHealthData,
-      tooltip: noHealthDataTooltip,
-    },
-    {
-      value: YAxis.SESSION_DURATION,
-      label: t('Session Duration'),
-      disabled: !hasHealthData,
-      tooltip: noHealthDataTooltip,
-    },
-    {
-      value: YAxis.USERS,
-      label: t('User Count'),
-      disabled: !hasHealthData,
-      tooltip: noHealthDataTooltip,
-    },
-    {
-      value: YAxis.CRASH_FREE,
-      label: t('Crash Free Rate'),
-      disabled: !hasHealthData,
-      tooltip: noHealthDataTooltip,
-    },
-    {
-      value: YAxis.FAILED_TRANSACTIONS,
-      label: t('Failure Count'),
-      disabled: !hasPerformance,
-      tooltip: noPerformanceTooltip,
-    },
-    {
-      value: YAxis.COUNT_DURATION,
-      label: t('Slow Duration Count'),
-      disabled: !hasPerformance,
-      tooltip: noPerformanceTooltip,
-    },
-    {
-      value: YAxis.COUNT_VITAL,
-      label: t('Slow Vital Count'),
-      disabled: !hasPerformance,
-      tooltip: noPerformanceTooltip,
-    },
-    {
-      value: YAxis.EVENTS,
-      label: t('Event Count'),
-      disabled: !hasDiscover && !hasPerformance,
-      tooltip: noDiscoverTooltip,
-    },
-  ];
-
-  const getSummaryHeading = () => {
-    switch (yAxis) {
-      case YAxis.USERS:
-        return t('Total Active Users');
-      case YAxis.CRASH_FREE:
-        return t('Average Rate');
-      case YAxis.SESSION_DURATION:
-        return t('Median Duration');
-      case YAxis.EVENTS:
-        return t('Total Events');
-      case YAxis.FAILED_TRANSACTIONS:
-        return t('Failed Transactions');
-      case YAxis.COUNT_DURATION:
-        return t('Count over %sms', organization.apdexThreshold);
-      case YAxis.COUNT_VITAL:
-        return vitalType !== WebVital.CLS
-          ? t('Count over %sms', WEB_VITAL_DETAILS[vitalType].poorThreshold)
-          : t('Count over %s', WEB_VITAL_DETAILS[vitalType].poorThreshold);
-      case YAxis.SESSIONS:
-      default:
-        return t('Total Sessions');
-    }
-  };
-
-  return (
-    <ChartControls>
-      <InlineContainer>
-        <SectionHeading key="total-label">
-          {getSummaryHeading()}
-          <QuestionTooltip
-            position="top"
-            size="sm"
-            title={t('This value includes only the current release.')}
-          />
-        </SectionHeading>
-        <SectionValue key="total-value">{summary}</SectionValue>
-      </InlineContainer>
-      <InlineContainer>
-        <SecondarySelector
-          yAxis={yAxis}
-          eventType={eventType}
-          onEventTypeChange={onEventTypeChange}
-          vitalType={vitalType}
-          onVitalTypeChange={onVitalTypeChange}
-        />
-        <OptionSelector
-          title={t('Display')}
-          selected={yAxis}
-          options={yAxisOptions}
-          onChange={onYAxisChange as (value: string) => void}
-        />
-      </InlineContainer>
-    </ChartControls>
-  );
-};
-
-const eventTypeOptions: SelectValue<EventType>[] = [
-  {value: EventType.ALL, label: t('All')},
-  {value: EventType.CSP, label: t('CSP')},
-  {value: EventType.DEFAULT, label: t('Default')},
-  {value: EventType.ERROR, label: 'Error'},
-  {value: EventType.TRANSACTION, label: t('Transaction')},
-];
-
-const vitalTypeOptions: SelectValue<WebVital>[] = [
-  WebVital.FP,
-  WebVital.FCP,
-  WebVital.LCP,
-  WebVital.FID,
-  WebVital.CLS,
-].map(vital => ({value: vital, label: WEB_VITAL_DETAILS[vital].name}));
-
-type SecondarySelectorProps = {
-  yAxis: YAxis;
-  eventType: EventType;
-  onEventTypeChange: (v: EventType) => void;
-  vitalType: WebVital;
-  onVitalTypeChange: (v: WebVital) => void;
-};
-
-function SecondarySelector({
-  yAxis,
-  eventType,
-  onEventTypeChange,
-  vitalType,
-  onVitalTypeChange,
-}: SecondarySelectorProps) {
-  switch (yAxis) {
-    case YAxis.EVENTS:
-      return (
-        <OptionSelector
-          title={t('Event Type')}
-          selected={eventType}
-          options={eventTypeOptions}
-          onChange={onEventTypeChange as (value: string) => void}
-        />
-      );
-    case YAxis.COUNT_VITAL:
-      return (
-        <OptionSelector
-          title={t('Vital')}
-          selected={vitalType}
-          options={vitalTypeOptions}
-          onChange={onVitalTypeChange as (value: string) => void}
-        />
-      );
-    default:
-      return null;
-  }
-}
-
-export default ReleaseChartControls;

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