Browse Source

feat(performance-starfish-db-widget): Minor iterations on feedback. (#55568)

For project: [Starfish db module widget in
Performance](https://www.notion.so/sentry/Performance-Widget-for-Starfish-DB-Module-468c303b1bb44d3287f7a7c29b64f272)

Iterated on feedback, under the feature flag
`performance-database-view`.

- Added loading indicator for loading state. 
- Updated query to not include '!span.op: db.redis' as it's done in the
backend now.
- Replaced `Truncate` component for span description row with
`SpanDescriptionCell` for better readability of full query:
<img width="1059" alt="Screenshot 2023-08-31 at 4 07 36 PM"
src="https://github.com/getsentry/sentry/assets/60121741/78bada3f-958a-4bc4-b119-a9cc2c832894">

- Added alpha feature badge 
- Widgets in the top row now have `4` expandable rows instead of `3`.
Adjusted chart height.
<img width="871" alt="Screenshot 2023-09-04 at 3 48 35 PM"
src="https://github.com/getsentry/sentry/assets/60121741/5e17c488-0ed6-4530-a173-a13cba9eb2eb">

---------

Co-authored-by: Abdullah Khan <abdullahkhan@PG9Y57YDXQ.local>
Abdkhan14 1 year ago
parent
commit
236ed18bc6

+ 2 - 2
static/app/views/performance/landing/index.spec.tsx

@@ -316,7 +316,7 @@ describe('Performance > Landing > Index', function () {
 
       render(<WrappedComponent data={data} withStaticFilters />, data.routerContext);
 
-      await waitForElementToBeRemoved(() => screen.queryByTestId('loading-indicator'));
+      await waitForElementToBeRemoved(() => screen.getAllByTestId('loading-indicator'));
       await userEvent.type(screen.getByPlaceholderText('Search Transactions'), '{enter}');
       expect(searchHandlerMock).toHaveBeenCalledWith('', 'transactionsOnly');
     });
@@ -343,7 +343,7 @@ describe('Performance > Landing > Index', function () {
 
       wrapper = render(<WrappedComponent data={data} />, data.routerContext);
 
-      await waitForElementToBeRemoved(() => screen.queryByTestId('loading-indicator'));
+      await waitForElementToBeRemoved(() => screen.getAllByTestId('loading-indicator'));
 
       expect(await screen.findByPlaceholderText('Search Transactions')).toHaveValue('');
     });

+ 1 - 0
static/app/views/performance/landing/queryBatcher.spec.tsx

@@ -34,6 +34,7 @@ function WrappedComponent({data, ...rest}) {
       <MEPSettingProvider>
         <PerformanceDisplayProvider value={{performanceType: ProjectPerformanceType.ANY}}>
           <WidgetContainer
+            chartHeight={100}
             allowedCharts={[
               PerformanceWidgetSetting.TPM_AREA,
               PerformanceWidgetSetting.FAILURE_RATE_AREA,

+ 1 - 1
static/app/views/performance/landing/views/allTransactionsView.tsx

@@ -16,7 +16,7 @@ export function AllTransactionsView(props: BasePerformanceViewProps) {
     props.organization.features.includes('performance-new-trends') &&
     canUseMetricsData(props.organization)
   ) {
-    if (props.organization.features.includes('starfish-view')) {
+    if (props.organization.features.includes('performance-database-view')) {
       doubleChartRowCharts.unshift(PerformanceWidgetSetting.MOST_RELATED_ISSUES);
       doubleChartRowCharts.unshift(PerformanceWidgetSetting.MOST_CHANGED);
       doubleChartRowCharts.unshift(PerformanceWidgetSetting.MOST_TIME_SPENT_DB_QUERIES);

+ 1 - 1
static/app/views/performance/landing/views/backendView.tsx

@@ -47,7 +47,7 @@ export function BackendView(props: BasePerformanceViewProps) {
       doubleChartRowCharts.push(PerformanceWidgetSetting.MOST_CHANGED);
     }
 
-    if (props.organization.features.includes('starfish-view')) {
+    if (props.organization.features.includes('performance-database-view')) {
       doubleChartRowCharts.unshift(PerformanceWidgetSetting.MOST_TIME_SPENT_DB_QUERIES);
     }
   } else {

+ 17 - 1
static/app/views/performance/landing/widgets/components/performanceWidget.tsx

@@ -2,6 +2,7 @@ import {Fragment, useCallback, useRef, useState} from 'react';
 import styled from '@emotion/styled';
 
 import ErrorPanel from 'sentry/components/charts/errorPanel';
+import LoadingIndicator from 'sentry/components/loadingIndicator';
 import Placeholder from 'sentry/components/placeholder';
 import {IconWarning} from 'sentry/icons/iconWarning';
 import {space} from 'sentry/styles/space';
@@ -144,7 +145,11 @@ export function DataDisplay<T extends WidgetDataConstraint>(
             })}
           </ContentContainer>
         ))}
-        loadingComponent={<PerformanceWidgetPlaceholder height={`${totalHeight}px`} />}
+        loadingComponent={
+          <LoadingWrapper height={totalHeight}>
+            <StyledLoadingIndicator size={40} />
+          </LoadingWrapper>
+        }
         emptyComponent={
           EmptyComponent ? (
             <EmptyComponent />
@@ -184,6 +189,17 @@ const PerformanceWidgetPlaceholder = styled(Placeholder)`
   border-bottom-left-radius: inherit;
 `;
 
+const LoadingWrapper = styled('div')<{height?: number}>`
+  height: ${p => p.height}px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+`;
+
+const StyledLoadingIndicator = styled(LoadingIndicator)`
+  margin: 0;
+`;
+
 GenericPerformanceWidget.defaultProps = {
   containerType: 'panel',
   chartHeight: 200,

+ 1 - 1
static/app/views/performance/landing/widgets/components/widgetChartRow.tsx

@@ -83,7 +83,7 @@ export function DoubleChartRow(props: ChartRowProps) {
 
 DoubleChartRow.defaultProps = {
   chartCount: 2,
-  chartHeight: 180,
+  chartHeight: 150,
 };
 
 const StyledRow = styled(PerformanceLayoutBodyRow)`

+ 54 - 8
static/app/views/performance/landing/widgets/components/widgetContainer.spec.tsx

@@ -16,6 +16,8 @@ import WidgetContainer from 'sentry/views/performance/landing/widgets/components
 import {PerformanceWidgetSetting} from 'sentry/views/performance/landing/widgets/widgetDefinitions';
 import {ProjectPerformanceType} from 'sentry/views/performance/utils';
 
+import {QUERY_LIMIT_PARAM} from '../utils';
+
 const initializeData = (query = {}, rest: InitializeDataSettings = {}) => {
   const data = _initializeData({
     query: {statsPeriod: '7d', environment: ['prod'], project: [-42], ...query},
@@ -39,6 +41,7 @@ function WrappedComponent({data, withStaticFilters = false, ...rest}) {
             value={{performanceType: ProjectPerformanceType.ANY}}
           >
             <WidgetContainer
+              chartHeight={100}
               allowedCharts={[
                 PerformanceWidgetSetting.TPM_AREA,
                 PerformanceWidgetSetting.FAILURE_RATE_AREA,
@@ -237,7 +240,7 @@ describe('Performance > Widgets > WidgetContainer', function () {
           interval: undefined,
           middle: undefined,
           noPagination: true,
-          per_page: 3,
+          per_page: QUERY_LIMIT_PARAM,
           project: ['-42'],
           query:
             'transaction.op:pageload tpm():>0.01 count_percentage():>0.25 count_percentage():<4 trend_percentage():>0% confidence():>6',
@@ -737,7 +740,7 @@ describe('Performance > Widgets > WidgetContainer', function () {
         query: expect.objectContaining({
           environment: ['prod'],
           field: ['transaction', 'project.id', 'failure_count()'],
-          per_page: 3,
+          per_page: QUERY_LIMIT_PARAM,
           project: ['-42'],
           query: 'transaction.op:pageload failure_count():>0',
           sort: '-failure_count()',
@@ -768,7 +771,7 @@ describe('Performance > Widgets > WidgetContainer', function () {
         query: expect.objectContaining({
           environment: ['prod'],
           field: ['issue', 'transaction', 'title', 'project.id', 'count()'],
-          per_page: 3,
+          per_page: QUERY_LIMIT_PARAM,
           project: ['-42'],
           query: 'event.type:error !tags[transaction]:"" count():>0',
           sort: '-count()',
@@ -832,7 +835,7 @@ describe('Performance > Widgets > WidgetContainer', function () {
           field: ['transaction', 'project'],
           interval: undefined,
           middle: undefined,
-          per_page: 3,
+          per_page: QUERY_LIMIT_PARAM,
           project: ['-42'],
           query:
             'transaction.op:pageload tpm():>0.01 count_percentage():>0.25 count_percentage():<4 trend_percentage():>0% confidence():>6',
@@ -845,6 +848,49 @@ describe('Performance > Widgets > WidgetContainer', function () {
     );
   });
 
+  it('Most time spent in db queries widget', async function () {
+    const data = initializeData();
+
+    wrapper = render(
+      <MEPSettingProvider forceTransactions>
+        <WrappedComponent
+          data={data}
+          defaultChartSetting={PerformanceWidgetSetting.MOST_TIME_SPENT_DB_QUERIES}
+        />
+      </MEPSettingProvider>
+    );
+
+    expect(await screen.findByTestId('performance-widget-title')).toHaveTextContent(
+      'Most Time Spent in DB Queries'
+    );
+    expect(eventsMock).toHaveBeenCalledTimes(1);
+    expect(eventsMock).toHaveBeenNthCalledWith(
+      1,
+      expect.anything(),
+      expect.objectContaining({
+        query: expect.objectContaining({
+          dataset: 'spansMetrics',
+          environment: ['prod'],
+          field: [
+            'span.op',
+            'span.group',
+            'project.id',
+            'span.description',
+            'sum(span.self_time)',
+            'avg(span.self_time)',
+            'time_spent_percentage()',
+          ],
+          per_page: QUERY_LIMIT_PARAM,
+          project: ['-42'],
+          query:
+            'has:span.description span.module:db transaction.op:http.server transaction.op:pageload',
+          sort: '-time_spent_percentage()',
+          statsPeriod: '7d',
+        }),
+      })
+    );
+  });
+
   it('Most regressed trends widget', async function () {
     const data = initializeData();
 
@@ -868,7 +914,7 @@ describe('Performance > Widgets > WidgetContainer', function () {
           field: ['transaction', 'project'],
           interval: undefined,
           middle: undefined,
-          per_page: 3,
+          per_page: QUERY_LIMIT_PARAM,
           project: ['-42'],
           query:
             'transaction.op:pageload tpm():>0.01 count_percentage():>0.25 count_percentage():<4 trend_percentage():>0% confidence():>6',
@@ -905,7 +951,7 @@ describe('Performance > Widgets > WidgetContainer', function () {
           environment: ['prod'],
           field: ['transaction', 'project.id', 'epm()', 'avg(measurements.frames_slow)'],
           noPagination: true,
-          per_page: 3,
+          per_page: QUERY_LIMIT_PARAM,
           project: ['-42'],
           query: 'transaction.op:pageload epm():>0.01 avg(measurements.frames_slow):>0',
           sort: '-avg(measurements.frames_slow)',
@@ -946,7 +992,7 @@ describe('Performance > Widgets > WidgetContainer', function () {
           environment: ['prod'],
           field: ['transaction', 'project.id', 'epm()', 'avg(measurements.frames_slow)'],
           noPagination: true,
-          per_page: 3,
+          per_page: QUERY_LIMIT_PARAM,
           project: ['-42'],
           query: 'transaction.op:pageload epm():>0.01 avg(measurements.frames_slow):>0',
           sort: '-avg(measurements.frames_slow)',
@@ -987,7 +1033,7 @@ describe('Performance > Widgets > WidgetContainer', function () {
             'avg(measurements.frames_frozen)',
           ],
           noPagination: true,
-          per_page: 3,
+          per_page: QUERY_LIMIT_PARAM,
           project: ['-42'],
           query: 'transaction.op:pageload epm():>0.01 avg(measurements.frames_frozen):>0',
           sort: '-avg(measurements.frames_frozen)',

+ 12 - 1
static/app/views/performance/landing/widgets/components/widgetHeader.tsx

@@ -1,6 +1,7 @@
 import styled from '@emotion/styled';
 
 import {HeaderTitleLegend} from 'sentry/components/charts/styles';
+import FeatureBadge from 'sentry/components/featureBadge';
 import QuestionTooltip from 'sentry/components/questionTooltip';
 import TextOverflow from 'sentry/components/textOverflow';
 import {space} from 'sentry/styles/space';
@@ -11,11 +12,15 @@ import {
   WidgetDataConstraint,
   WidgetDataProps,
 } from '../types';
+import {PerformanceWidgetSetting} from '../widgetDefinitions';
 
 export function WidgetHeader<T extends WidgetDataConstraint>(
   props: GenericPerformanceWidgetProps<T> & WidgetDataProps<T>
 ) {
-  const {title, titleTooltip, Subtitle, HeaderActions, InteractiveTitle} = props;
+  const {title, titleTooltip, Subtitle, HeaderActions, InteractiveTitle, chartSetting} =
+    props;
+  const isStarfishDBWidget =
+    chartSetting === PerformanceWidgetSetting.MOST_TIME_SPENT_DB_QUERIES;
   return (
     <WidgetHeaderContainer>
       <TitleContainer>
@@ -25,6 +30,7 @@ export function WidgetHeader<T extends WidgetDataConstraint>(
           ) : (
             <TextOverflow>{title}</TextOverflow>
           )}
+          {isStarfishDBWidget && <FeatureBadge type="alpha" />}
           <MEPTag />
           {titleTooltip && (
             <QuestionTooltip position="top" size="sm" title={titleTooltip} />
@@ -43,6 +49,11 @@ const StyledHeaderTitleLegend = styled(HeaderTitleLegend)`
   position: relative;
   z-index: initial;
   top: -${space(0.5)};
+
+  ${FeatureBadge} {
+    position: relative;
+    top: -${space(0.25)};
+  }
 `;
 
 const TitleContainer = styled('div')`

+ 3 - 1
static/app/views/performance/landing/widgets/transforms/transformTrendsDiscover.tsx

@@ -1,6 +1,8 @@
 import {TrendDiscoveryChildrenProps} from 'sentry/utils/performance/trends/trendsDiscoverQuery';
 import {normalizeTrends} from 'sentry/views/performance/trends/utils';
 
+import {QUERY_LIMIT_PARAM} from '../utils';
+
 export function transformTrendsDiscover(_: any, props: TrendDiscoveryChildrenProps) {
   const {trendsData} = props;
   const events = trendsData
@@ -15,7 +17,7 @@ export function transformTrendsDiscover(_: any, props: TrendDiscoveryChildrenPro
     isErrored: !!props.error,
     errored: props.error,
     statsData: trendsData ? trendsData.stats : {},
-    transactionsList: events && events.slice ? events.slice(0, 3) : [],
+    transactionsList: events && events.slice ? events.slice(0, QUERY_LIMIT_PARAM) : [],
     events,
   };
 }

+ 4 - 0
static/app/views/performance/landing/widgets/utils.tsx

@@ -11,6 +11,10 @@ import {ProjectPerformanceType} from '../../utils';
 
 import {PerformanceWidgetSetting} from './widgetDefinitions';
 
+export const QUERY_LIMIT_PARAM = 4;
+
+export const TOTAL_EXPANDABLE_ROWS_HEIGHT = 37 * QUERY_LIMIT_PARAM;
+
 export const eventsRequestQueryProps = [
   'children',
   'organization',

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