Browse Source

fix(replays): Adjust highlight timestamp of console row at hovering over them (#37510)

### Changes
- Fixed the type for the console component from `BreadcrumbTypeDefault` to `Crumb`
- Changed the function to get the closest crumb at hovering over a row timestamp
- Added hover and click functionality to all columns instead of just having it on the timestamp

Closes #36014 
Closes #37358
Jesus Padron 2 years ago
parent
commit
68659cc63d

+ 2 - 2
static/app/types/breadcrumbs.tsx

@@ -107,7 +107,7 @@ export type Crumb = RawCrumb & {
 };
 
 export function isBreadcrumbTypeDefault(
-  breadcrumb: RawCrumb
-): breadcrumb is BreadcrumbTypeDefault {
+  breadcrumb: Crumb
+): breadcrumb is Extract<Crumb, BreadcrumbTypeDefault> {
   return ![BreadcrumbType.HTTP, BreadcrumbType.NAVIGATION].includes(breadcrumb.type);
 }

+ 19 - 7
static/app/views/replays/detail/console/consoleMessage.tsx

@@ -12,11 +12,11 @@ import {relativeTimeInMs, showPlayerTime} from 'sentry/components/replays/utils'
 import Tooltip from 'sentry/components/tooltip';
 import {IconClose, IconWarning} from 'sentry/icons';
 import space from 'sentry/styles/space';
-import {BreadcrumbTypeDefault} from 'sentry/types/breadcrumbs';
+import {BreadcrumbTypeDefault, Crumb} from 'sentry/types/breadcrumbs';
 import {objectIsEmpty} from 'sentry/utils';
 
 interface MessageFormatterProps {
-  breadcrumb: BreadcrumbTypeDefault;
+  breadcrumb: Extract<Crumb, BreadcrumbTypeDefault>;
 }
 
 /**
@@ -143,10 +143,18 @@ function ConsoleMessage({
         level={breadcrumb.level}
         isActive={isActive}
         hasOccurred={hasOccurred}
+        onMouseOver={handleOnMouseOver}
+        onMouseOut={handleOnMouseOut}
       >
         {ICONS[breadcrumb.level]}
       </Icon>
-      <Message isLast={isLast} level={breadcrumb.level} hasOccurred={hasOccurred}>
+      <Message
+        isLast={isLast}
+        level={breadcrumb.level}
+        hasOccurred={hasOccurred}
+        onMouseOver={handleOnMouseOver}
+        onMouseOut={handleOnMouseOut}
+      >
         <ErrorBoundary mini>
           <MessageFormatter breadcrumb={breadcrumb} />
         </ErrorBoundary>
@@ -157,13 +165,13 @@ function ConsoleMessage({
         hasOccurred={hasOccurred}
       >
         <Tooltip title={<DateTime date={breadcrumb.timestamp} seconds />}>
-          <div
+          <ConsoleTimestampButton
             onClick={handleOnClick}
             onMouseOver={handleOnMouseOver}
             onMouseOut={handleOnMouseOut}
           >
             {showPlayerTime(breadcrumb.timestamp || '', startTimestampMs)}
-          </div>
+          </ConsoleTimestampButton>
         </Tooltip>
       </ConsoleTimestamp>
     </Fragment>
@@ -212,9 +220,13 @@ const Common = styled('div')<{
   }
 `;
 
-const ConsoleTimestamp = styled(Common)<{isLast: boolean; level: string}>`
+const ConsoleTimestamp = styled(Common)`
   padding: ${space(0.25)} ${space(1)};
-  cursor: pointer;
+`;
+
+const ConsoleTimestampButton = styled('button')`
+  background: none;
+  border: none;
 `;
 
 const Icon = styled(Common)<{isActive: boolean}>`

+ 19 - 42
static/app/views/replays/detail/console/index.tsx

@@ -5,21 +5,26 @@ import debounce from 'lodash/debounce';
 import CompactSelect from 'sentry/components/forms/compactSelect';
 import {Panel} from 'sentry/components/panels';
 import {useReplayContext} from 'sentry/components/replays/replayContext';
-import {relativeTimeInMs, showPlayerTime} from 'sentry/components/replays/utils';
+import {relativeTimeInMs} from 'sentry/components/replays/utils';
 import SearchBar from 'sentry/components/searchBar';
 import {t} from 'sentry/locale';
 import space from 'sentry/styles/space';
-import type {BreadcrumbLevelType, BreadcrumbTypeDefault} from 'sentry/types/breadcrumbs';
+import type {
+  BreadcrumbLevelType,
+  BreadcrumbTypeDefault,
+  Crumb,
+} from 'sentry/types/breadcrumbs';
+import {getPrevBreadcrumb} from 'sentry/utils/replays/getBreadcrumb';
 import ConsoleMessage from 'sentry/views/replays/detail/console/consoleMessage';
 import {filterBreadcrumbs} from 'sentry/views/replays/detail/console/utils';
 import EmptyMessage from 'sentry/views/settings/components/emptyMessage';
 
 interface Props {
-  breadcrumbs: BreadcrumbTypeDefault[];
+  breadcrumbs: Extract<Crumb, BreadcrumbTypeDefault>[];
   startTimestampMs: number;
 }
 
-const getDistinctLogLevels = (breadcrumbs: BreadcrumbTypeDefault[]) =>
+const getDistinctLogLevels = (breadcrumbs: Crumb[]) =>
   Array.from(new Set<string>(breadcrumbs.map(breadcrumb => breadcrumb.level)));
 
 function Console({breadcrumbs, startTimestampMs = 0}: Props) {
@@ -33,42 +38,14 @@ function Console({breadcrumbs, startTimestampMs = 0}: Props) {
     [logLevel, searchTerm, breadcrumbs]
   );
 
-  const activeConsoleBounds = useMemo(() => {
-    if (filteredBreadcrumbs.length <= 0 || currentHoverTime === undefined) {
-      return [-1, -1];
-    }
-
-    let indexUpperBound = 0;
-    const finalBreadCrumbIndex = filteredBreadcrumbs.length - 1;
-    const finalBreadcrumbTimestamp =
-      filteredBreadcrumbs[finalBreadCrumbIndex].timestamp || '';
-
-    if (
-      currentHoverTime >= relativeTimeInMs(finalBreadcrumbTimestamp, startTimestampMs)
-    ) {
-      indexUpperBound = finalBreadCrumbIndex;
-    } else {
-      indexUpperBound =
-        filteredBreadcrumbs.findIndex(
-          breadcrumb =>
-            relativeTimeInMs(breadcrumb.timestamp || '', startTimestampMs) >=
-            (currentHoverTime || 0)
-        ) - 1;
-    }
-
-    const activeMessageBoundary = showPlayerTime(
-      filteredBreadcrumbs[indexUpperBound]?.timestamp || '',
-      startTimestampMs
-    );
-
-    const indexLowerBound = filteredBreadcrumbs.findIndex(
-      breadcrumb =>
-        showPlayerTime(breadcrumb.timestamp || '', startTimestampMs) ===
-        activeMessageBoundary
-    );
-
-    return [indexLowerBound, indexUpperBound];
-  }, [currentHoverTime, filteredBreadcrumbs, startTimestampMs]);
+  const closestUserAction =
+    currentHoverTime !== undefined
+      ? getPrevBreadcrumb({
+          crumbs: breadcrumbs,
+          targetTimestampMs: startTimestampMs + (currentHoverTime ?? 0),
+          allowExact: true,
+        })
+      : undefined;
 
   return (
     <Fragment>
@@ -94,9 +71,9 @@ function Console({breadcrumbs, startTimestampMs = 0}: Props) {
           {filteredBreadcrumbs.map((breadcrumb, i) => {
             return (
               <ConsoleMessage
-                isActive={i >= activeConsoleBounds[0] && i <= activeConsoleBounds[1]}
+                isActive={closestUserAction?.id === breadcrumb.id}
                 startTimestampMs={startTimestampMs}
-                key={i}
+                key={breadcrumb.id}
                 isLast={i === breadcrumbs.length - 1}
                 breadcrumb={breadcrumb}
                 hasOccurred={

+ 2 - 2
static/app/views/replays/detail/console/utils.tsx

@@ -1,7 +1,7 @@
-import {BreadcrumbTypeDefault} from 'sentry/types/breadcrumbs';
+import {BreadcrumbTypeDefault, Crumb} from 'sentry/types/breadcrumbs';
 
 export const filterBreadcrumbs = (
-  breadcrumbs: BreadcrumbTypeDefault[],
+  breadcrumbs: Extract<Crumb, BreadcrumbTypeDefault>[],
   searchTerm: string,
   logLevel: Array<string>
 ) => {

+ 2 - 2
static/app/views/replays/detail/focusArea.tsx

@@ -5,7 +5,7 @@ import FeatureDisabled from 'sentry/components/acl/featureDisabled';
 import Placeholder from 'sentry/components/placeholder';
 import {useReplayContext} from 'sentry/components/replays/replayContext';
 import {t} from 'sentry/locale';
-import type {RawCrumb} from 'sentry/types/breadcrumbs';
+import type {Crumb} from 'sentry/types/breadcrumbs';
 import {isBreadcrumbTypeDefault} from 'sentry/types/breadcrumbs';
 import useActiveReplayTab from 'sentry/utils/replays/hooks/useActiveReplayTab';
 import useOrganization from 'sentry/utils/useOrganization';
@@ -18,7 +18,7 @@ import Trace from 'sentry/views/replays/detail/trace';
 
 type Props = {};
 
-function getBreadcrumbsByCategory(breadcrumbs: RawCrumb[], categories: string[]) {
+function getBreadcrumbsByCategory(breadcrumbs: Crumb[], categories: string[]) {
   return breadcrumbs
     .filter(isBreadcrumbTypeDefault)
     .filter(breadcrumb => categories.includes(breadcrumb.category || ''));

+ 20 - 1
tests/js/spec/views/replays/detail/console/messageFormatter.spec.tsx

@@ -4,10 +4,11 @@ import {
   BreadcrumbLevelType,
   BreadcrumbType,
   BreadcrumbTypeDefault,
+  Crumb,
 } from 'sentry/types/breadcrumbs';
 import {MessageFormatter} from 'sentry/views/replays/detail/console/consoleMessage';
 
-const breadcrumbs: BreadcrumbTypeDefault[] = [
+const breadcrumbs: Extract<Crumb, BreadcrumbTypeDefault>[] = [
   {
     type: BreadcrumbType.DEBUG,
     category: 'console',
@@ -18,6 +19,9 @@ const breadcrumbs: BreadcrumbTypeDefault[] = [
     level: BreadcrumbLevelType.LOG,
     message: 'This is a %s test',
     timestamp: '2022-06-22T20:00:39.959Z',
+    id: 1,
+    color: 'purple300',
+    description: 'Debug',
   },
   {
     type: BreadcrumbType.DEBUG,
@@ -29,6 +33,9 @@ const breadcrumbs: BreadcrumbTypeDefault[] = [
     level: BreadcrumbLevelType.LOG,
     message: 'test 1 false [object Object]',
     timestamp: '2022-06-22T16:49:11.198Z',
+    id: 2,
+    color: 'purple300',
+    description: 'Debug',
   },
   {
     type: BreadcrumbType.DEBUG,
@@ -40,6 +47,9 @@ const breadcrumbs: BreadcrumbTypeDefault[] = [
     level: BreadcrumbLevelType.ERROR,
     message: 'Error: this is my error message',
     timestamp: '2022-06-22T20:00:39.958Z',
+    id: 2,
+    color: 'purple300',
+    description: 'Debug',
   },
   {
     type: BreadcrumbType.DEBUG,
@@ -50,6 +60,9 @@ const breadcrumbs: BreadcrumbTypeDefault[] = [
     },
     level: BreadcrumbLevelType.ERROR,
     timestamp: '2022-06-22T20:00:39.958Z',
+    id: 3,
+    color: 'red300',
+    description: 'Error',
   },
   {
     type: BreadcrumbType.DEBUG,
@@ -67,6 +80,9 @@ const breadcrumbs: BreadcrumbTypeDefault[] = [
     level: BreadcrumbLevelType.LOG,
     message: '%c prev state color: #9E9E9E; font-weight: bold [object Object]',
     timestamp: '2022-06-09T00:50:25.273Z',
+    id: 4,
+    color: 'purple300',
+    description: 'Debug',
   },
   {
     type: BreadcrumbType.DEBUG,
@@ -78,6 +94,9 @@ const breadcrumbs: BreadcrumbTypeDefault[] = [
     level: BreadcrumbLevelType.LOG,
     message: 'test foo,bar',
     timestamp: '2022-06-23T17:09:31.158Z',
+    id: 5,
+    color: 'purple300',
+    description: 'Debug',
   },
 ];