From 410301a134f50993f416846351afefe553cc53e8 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Fri, 3 Jan 2025 14:47:05 -0500 Subject: [PATCH 1/3] feat(insights): add tooltips user misery and apdex --- static/app/utils/discover/fieldRenderers.tsx | 27 ++++++++++-- .../pages/backend/backendOverviewPage.tsx | 20 +++++++-- .../pages/frontend/frontendOverviewPage.tsx | 9 ++-- .../pages/mobile/mobileOverviewPage.tsx | 3 +- static/app/views/performance/data.tsx | 12 +++++- static/app/views/performance/table.tsx | 42 +++++++++++-------- 6 files changed, 84 insertions(+), 29 deletions(-) diff --git a/static/app/utils/discover/fieldRenderers.tsx b/static/app/utils/discover/fieldRenderers.tsx index 674c933c411364..a72906d9981906 100644 --- a/static/app/utils/discover/fieldRenderers.tsx +++ b/static/app/utils/discover/fieldRenderers.tsx @@ -20,7 +20,8 @@ import {Tooltip} from 'sentry/components/tooltip'; import UserMisery from 'sentry/components/userMisery'; import Version from 'sentry/components/version'; import {IconDownload} from 'sentry/icons'; -import {t} from 'sentry/locale'; +import {t, tct} from 'sentry/locale'; + import {space} from 'sentry/styles/space'; import type {IssueAttachment} from 'sentry/types/group'; import type {Organization} from 'sentry/types/organization'; @@ -132,6 +133,14 @@ const EmptyValueContainer = styled('span')` `; const emptyValue = {t('(no value)')}; const emptyStringValue = {t('(empty string)')}; +const missingUserMisery = tct( + 'We were unable to calculate User Misery. A likely cause of this is that the user was not set. [link:Read the docs]', + { + link: ( + + ), + } +); export function nullableValue(value: string | null): string | React.ReactElement { switch (value) { @@ -783,12 +792,24 @@ const SPECIAL_FUNCTIONS: SpecialFunctions = { const userMiseryField = fieldName; if (!(userMiseryField in data)) { - return {emptyValue}; + return ( + + + {emptyValue} + + + ); } const userMisery = data[userMiseryField]; if (userMisery === null || isNaN(userMisery)) { - return {emptyValue}; + return ( + + + {emptyValue} + + + ); } const projectThresholdConfig = 'project_threshold_config'; diff --git a/static/app/views/insights/pages/backend/backendOverviewPage.tsx b/static/app/views/insights/pages/backend/backendOverviewPage.tsx index ff19a33302a390..180d0a5aa85d01 100644 --- a/static/app/views/insights/pages/backend/backendOverviewPage.tsx +++ b/static/app/views/insights/pages/backend/backendOverviewPage.tsx @@ -3,6 +3,7 @@ import styled from '@emotion/styled'; import Feature from 'sentry/components/acl/feature'; import {COL_WIDTH_UNDEFINED} from 'sentry/components/gridEditable'; import * as Layout from 'sentry/components/layouts/thirds'; +import ExternalLink from 'sentry/components/links/externalLink'; import {NoAccess} from 'sentry/components/noAccess'; import {DatePageFilter} from 'sentry/components/organizations/datePageFilter'; import {EnvironmentPageFilter} from 'sentry/components/organizations/environmentPageFilter'; @@ -10,6 +11,7 @@ import PageFilterBar from 'sentry/components/organizations/pageFilterBar'; import {ProjectPageFilter} from 'sentry/components/organizations/projectPageFilter'; import TransactionNameSearchBar from 'sentry/components/performance/searchBar'; import * as TeamKeyTransactionManager from 'sentry/components/performance/teamKeyTransactionsManager'; +import {tct} from 'sentry/locale'; import {trackAnalytics} from 'sentry/utils/analytics'; import { canUseMetricsData, @@ -32,7 +34,10 @@ import {BACKEND_LANDING_TITLE} from 'sentry/views/insights/pages/backend/setting import {DomainOverviewPageProviders} from 'sentry/views/insights/pages/domainOverviewPageProviders'; import {OVERVIEW_PAGE_ALLOWED_OPS as FRONTEND_OVERVIEW_PAGE_OPS} from 'sentry/views/insights/pages/frontend/settings'; import {OVERVIEW_PAGE_ALLOWED_OPS as BACKEND_OVERVIEW_PAGE_OPS} from 'sentry/views/insights/pages/mobile/settings'; -import {generateBackendPerformanceEventView} from 'sentry/views/performance/data'; +import { + generateBackendPerformanceEventView, + USER_MISERY_TOOLTIP, +} from 'sentry/views/performance/data'; import { DoubleChartRow, TripleChartRow, @@ -46,6 +51,15 @@ import { ProjectPerformanceType, } from 'sentry/views/performance/utils'; +const APDEX_TOOLTIP = tct( + 'An industry-standard metric used to measure user satisfaction based on your application response times. [link:Learn more.]', + { + link: ( + + ), + } +); + export const BACKEND_COLUMN_TITLES = [ {title: 'http method'}, {title: 'transaction'}, @@ -55,9 +69,9 @@ export const BACKEND_COLUMN_TITLES = [ {title: 'p50()'}, {title: 'p95()'}, {title: 'failure rate'}, - {title: 'apdex'}, + {title: 'apdex', tooltip: APDEX_TOOLTIP}, {title: 'users'}, - {title: 'user misery'}, + {title: 'user misery', tooltip: USER_MISERY_TOOLTIP}, ]; function BackendOverviewPage() { diff --git a/static/app/views/insights/pages/frontend/frontendOverviewPage.tsx b/static/app/views/insights/pages/frontend/frontendOverviewPage.tsx index 9a081f2e7a3e4b..22fde4b8bd1baf 100644 --- a/static/app/views/insights/pages/frontend/frontendOverviewPage.tsx +++ b/static/app/views/insights/pages/frontend/frontendOverviewPage.tsx @@ -35,7 +35,10 @@ import { FRONTEND_LANDING_TITLE, OVERVIEW_PAGE_ALLOWED_OPS, } from 'sentry/views/insights/pages/frontend/settings'; -import {generateFrontendOtherPerformanceEventView} from 'sentry/views/performance/data'; +import { + generateFrontendOtherPerformanceEventView, + USER_MISERY_TOOLTIP, +} from 'sentry/views/performance/data'; import { DoubleChartRow, TripleChartRow, @@ -50,7 +53,7 @@ import { } from 'sentry/views/performance/utils'; const DURATION_TOOLTIP = tct( - 'A heuristic measuring when a pageload or navigation completes. Based on whether the initial load of the webpage has become idle [link:Learn more.]', + 'A heuristic measuring when a pageload or navigation completes. Based on whether the initial load of the webpage has become idle. [link:Learn more.]', { link: ( @@ -67,7 +70,7 @@ export const FRONTEND_COLUMN_TITLES = [ {title: 'p75()', tooltip: DURATION_TOOLTIP}, {title: 'p95()', tooltip: DURATION_TOOLTIP}, {title: 'users'}, - {title: 'user misery'}, + {title: 'user misery', tooltip: USER_MISERY_TOOLTIP}, ]; function FrontendOverviewPage() { diff --git a/static/app/views/insights/pages/mobile/mobileOverviewPage.tsx b/static/app/views/insights/pages/mobile/mobileOverviewPage.tsx index 393375c6feb6ec..cfde47ea8a27ee 100644 --- a/static/app/views/insights/pages/mobile/mobileOverviewPage.tsx +++ b/static/app/views/insights/pages/mobile/mobileOverviewPage.tsx @@ -35,6 +35,7 @@ import { import { generateGenericPerformanceEventView, generateMobilePerformanceEventView, + USER_MISERY_TOOLTIP, } from 'sentry/views/performance/data'; import {checkIsReactNative} from 'sentry/views/performance/landing/utils'; import { @@ -58,7 +59,7 @@ const MOBILE_COLUMN_TITLES = [ {title: 'slow frame %'}, {title: 'frozen frame %'}, {title: 'users'}, - {title: 'user misery'}, + {title: 'user misery', tooltip: USER_MISERY_TOOLTIP}, ]; const REACT_NATIVE_COLUMN_TITLES = [ diff --git a/static/app/views/performance/data.tsx b/static/app/views/performance/data.tsx index 0d2e044b5e04a1..672a142f4b4fa8 100644 --- a/static/app/views/performance/data.tsx +++ b/static/app/views/performance/data.tsx @@ -3,7 +3,8 @@ import type {Location} from 'history'; import {COL_WIDTH_UNDEFINED} from 'sentry/components/gridEditable'; import {wrapQueryInWildcards} from 'sentry/components/performance/searchBar'; import {ALL_ACCESS_PROJECTS} from 'sentry/constants/pageFilters'; -import {t} from 'sentry/locale'; +import ExternalLink from 'sentry/components/links/externalLink'; +import {t, tct} from 'sentry/locale'; import type {SelectValue} from 'sentry/types/core'; import type {NewQuery, Organization} from 'sentry/types/organization'; import type {Project} from 'sentry/types/project'; @@ -36,6 +37,15 @@ export const COLUMN_TITLES = [ 'user misery', ]; +export const USER_MISERY_TOOLTIP = tct( + 'A configurable score telling you how frequently users are frustrated by your application performance. [link:Learn more.]', + { + link: ( + + ), + } +); + const TOKEN_KEYS_SUPPORTED_IN_LIMITED_SEARCH = ['transaction']; export const getDefaultStatsPeriod = (organization: Organization) => { diff --git a/static/app/views/performance/table.tsx b/static/app/views/performance/table.tsx index a88bbf69a39433..230c25a12e7380 100644 --- a/static/app/views/performance/table.tsx +++ b/static/app/views/performance/table.tsx @@ -11,11 +11,9 @@ import SortLink from 'sentry/components/gridEditable/sortLink'; import Link from 'sentry/components/links/link'; import LoadingIndicator from 'sentry/components/loadingIndicator'; import Pagination from 'sentry/components/pagination'; -import QuestionTooltip from 'sentry/components/questionTooltip'; import {Tooltip} from 'sentry/components/tooltip'; import {IconStar} from 'sentry/icons'; import {t, tct} from 'sentry/locale'; -import {space} from 'sentry/styles/space'; import type {Organization} from 'sentry/types/organization'; import type {Project} from 'sentry/types/project'; import {trackAnalytics} from 'sentry/utils/analytics'; @@ -456,21 +454,37 @@ class _Table extends Component { /> ); if (field.field.startsWith('user_misery')) { - return ( - - {sortLink} - - ); + if (title.tooltip) { + { + title.tooltip ? ( + + ) : ( + '' + ); + } + return ( + + + {sortLink} + + + ); + } else { + return ( + + {sortLink} + + ); + } } if (!title.tooltip) { return sortLink; } return ( -
+ {sortLink} - -
+ ); } @@ -649,12 +663,4 @@ const UnparameterizedTooltipWrapper = styled('div')` justify-content: center; `; -const Header = styled('div')` - display: grid; - grid-template-columns: repeat(2, max-content); - align-items: center; - padding: ${space(1.5)}; - grid-column-gap: ${space(0.5)}; -`; - export default Table; From a2fd5226c46514a34b38e7aeb256f57a4e074788 Mon Sep 17 00:00:00 2001 From: Benjamin Coe Date: Fri, 3 Jan 2025 15:22:30 -0500 Subject: [PATCH 2/3] chore: address code review --- static/app/utils/discover/fieldRenderers.tsx | 8 ++------ static/app/views/performance/table.tsx | 7 ------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/static/app/utils/discover/fieldRenderers.tsx b/static/app/utils/discover/fieldRenderers.tsx index a72906d9981906..ba0d119ab5cda5 100644 --- a/static/app/utils/discover/fieldRenderers.tsx +++ b/static/app/utils/discover/fieldRenderers.tsx @@ -794,9 +794,7 @@ const SPECIAL_FUNCTIONS: SpecialFunctions = { if (!(userMiseryField in data)) { return ( - - {emptyValue} - + {emptyValue} ); } @@ -805,9 +803,7 @@ const SPECIAL_FUNCTIONS: SpecialFunctions = { if (userMisery === null || isNaN(userMisery)) { return ( - - {emptyValue} - + {emptyValue} ); } diff --git a/static/app/views/performance/table.tsx b/static/app/views/performance/table.tsx index 230c25a12e7380..6aa8e5f162ffdb 100644 --- a/static/app/views/performance/table.tsx +++ b/static/app/views/performance/table.tsx @@ -455,13 +455,6 @@ class _Table extends Component { ); if (field.field.startsWith('user_misery')) { if (title.tooltip) { - { - title.tooltip ? ( - - ) : ( - '' - ); - } return ( From defb920fa2083af9f3921896fd4dcbe693b9d7e1 Mon Sep 17 00:00:00 2001 From: "getsantry[bot]" <66042841+getsantry[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2025 20:23:55 +0000 Subject: [PATCH 3/3] :hammer_and_wrench: apply pre-commit fixes --- static/app/utils/discover/fieldRenderers.tsx | 1 - static/app/views/performance/data.tsx | 2 +- static/app/views/performance/table.tsx | 11 +++++------ 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/static/app/utils/discover/fieldRenderers.tsx b/static/app/utils/discover/fieldRenderers.tsx index ba0d119ab5cda5..1218ff69fcf3be 100644 --- a/static/app/utils/discover/fieldRenderers.tsx +++ b/static/app/utils/discover/fieldRenderers.tsx @@ -21,7 +21,6 @@ import UserMisery from 'sentry/components/userMisery'; import Version from 'sentry/components/version'; import {IconDownload} from 'sentry/icons'; import {t, tct} from 'sentry/locale'; - import {space} from 'sentry/styles/space'; import type {IssueAttachment} from 'sentry/types/group'; import type {Organization} from 'sentry/types/organization'; diff --git a/static/app/views/performance/data.tsx b/static/app/views/performance/data.tsx index 672a142f4b4fa8..42ed4fb691ebc5 100644 --- a/static/app/views/performance/data.tsx +++ b/static/app/views/performance/data.tsx @@ -1,9 +1,9 @@ import type {Location} from 'history'; import {COL_WIDTH_UNDEFINED} from 'sentry/components/gridEditable'; +import ExternalLink from 'sentry/components/links/externalLink'; import {wrapQueryInWildcards} from 'sentry/components/performance/searchBar'; import {ALL_ACCESS_PROJECTS} from 'sentry/constants/pageFilters'; -import ExternalLink from 'sentry/components/links/externalLink'; import {t, tct} from 'sentry/locale'; import type {SelectValue} from 'sentry/types/core'; import type {NewQuery, Organization} from 'sentry/types/organization'; diff --git a/static/app/views/performance/table.tsx b/static/app/views/performance/table.tsx index 9fff8323d5cac4..33720623912224 100644 --- a/static/app/views/performance/table.tsx +++ b/static/app/views/performance/table.tsx @@ -462,13 +462,12 @@ class _Table extends Component { ); - } else { - return ( - - {sortLink} - - ); } + return ( + + {sortLink} + + ); } if (!title.tooltip) {