Browse Source

fix(breadcrumbs): Apply breadcrumb meta highlighting to correct breadcrumbs when sorting/filtering (#46520)

The breadcrumb items were matching to the incorrect `meta` data which
resulted in the wrong items getting highlighted. This change ties the
item up with the meta before any sorts/filters are applied to prevent
that from happening.

Before:

<img width="936" alt="CleanShot 2023-03-29 at 09 39 03@2x"
src="https://user-images.githubusercontent.com/10888943/228608506-f6e26997-1a7a-4397-b141-4c940de5a6f5.png">

After:

<img width="868" alt="CleanShot 2023-03-29 at 09 39 39@2x"
src="https://user-images.githubusercontent.com/10888943/228608571-0c8d202c-4b0d-4859-ae9a-df54c39b0d89.png">
Malachi Willey 1 year ago
parent
commit
3b2d97d9d1

+ 2 - 1
static/app/components/events/interfaces/breadcrumbs/breadcrumb/data/index.tsx

@@ -1,3 +1,4 @@
+import {BreadcrumbMeta} from 'sentry/components/events/interfaces/breadcrumbs/types';
 import {Organization} from 'sentry/types';
 import {BreadcrumbType, RawCrumb} from 'sentry/types/breadcrumbs';
 import {Event} from 'sentry/types/event';
@@ -11,7 +12,7 @@ type Props = {
   event: Event;
   organization: Organization;
   searchTerm: string;
-  meta?: Record<any, any>;
+  meta?: BreadcrumbMeta;
 };
 
 export function Data({breadcrumb, event, organization, searchTerm, meta}: Props) {

+ 5 - 9
static/app/components/events/interfaces/breadcrumbs/breadcrumbs.tsx

@@ -8,13 +8,12 @@ import {
 } from 'react-virtualized';
 import styled from '@emotion/styled';
 
+import {BreadcrumbWithMeta} from 'sentry/components/events/interfaces/breadcrumbs/types';
 import {PanelTable} from 'sentry/components/panels';
 import {Tooltip} from 'sentry/components/tooltip';
 import {IconSort} from 'sentry/icons';
 import {t} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
-import {EntryType} from 'sentry/types';
-import {Crumb} from 'sentry/types/breadcrumbs';
 import {useResizableDrawer} from 'sentry/utils/useResizableDrawer';
 
 import {Breadcrumb} from './breadcrumb';
@@ -31,7 +30,7 @@ type Props = Pick<
   React.ComponentProps<typeof Breadcrumb>,
   'event' | 'organization' | 'searchTerm' | 'relativeTime' | 'displayRelativeTime'
 > & {
-  breadcrumbs: Crumb[];
+  breadcrumbs: BreadcrumbWithMeta[];
   emptyMessage: Pick<
     React.ComponentProps<typeof PanelTable>,
     'emptyMessage' | 'emptyAction'
@@ -50,9 +49,6 @@ function Breadcrumbs({
   emptyMessage,
 }: Props) {
   const [scrollbarSize, setScrollbarSize] = useState(0);
-  const entryIndex = event.entries.findIndex(
-    entry => entry.type === EntryType.BREADCRUMBS
-  );
 
   const listRef = useRef<List>(null);
   const contentRef = useRef<HTMLDivElement>(null);
@@ -81,8 +77,8 @@ function Breadcrumbs({
   });
 
   function renderRow({index, key, parent, style}: ListRowProps) {
-    const breadcrumb = breadcrumbs[index];
-    const isLastItem = breadcrumbs[0].id === breadcrumb.id;
+    const {breadcrumb, meta} = breadcrumbs[index];
+    const isLastItem = index === breadcrumbs.length - 1;
     const {height} = style;
     return (
       <CellMeasurer
@@ -100,7 +96,7 @@ function Breadcrumbs({
             organization={organization}
             searchTerm={searchTerm}
             breadcrumb={breadcrumb}
-            meta={event._meta?.entries?.[entryIndex]?.data?.values?.[index]}
+            meta={meta}
             event={event}
             relativeTime={relativeTime}
             displayRelativeTime={displayRelativeTime}

+ 31 - 15
static/app/components/events/interfaces/breadcrumbs/index.tsx

@@ -13,11 +13,12 @@ import {
 import ErrorBoundary from 'sentry/components/errorBoundary';
 import {EventDataSection} from 'sentry/components/events/eventDataSection';
 import EventReplay from 'sentry/components/events/eventReplay';
+import {BreadcrumbWithMeta} from 'sentry/components/events/interfaces/breadcrumbs/types';
 import {IconSort} from 'sentry/icons';
 import {t} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
 import {Organization} from 'sentry/types';
-import {BreadcrumbLevelType, Crumb, RawCrumb} from 'sentry/types/breadcrumbs';
+import {BreadcrumbLevelType, RawCrumb} from 'sentry/types/breadcrumbs';
 import {EntryType, Event} from 'sentry/types/event';
 import {defined} from 'sentry/utils';
 import {useLocalStorageState} from 'sentry/utils/useLocalStorageState';
@@ -62,6 +63,10 @@ function BreadcrumbsContainer({data, event, organization, projectSlug, isShare}:
     BreadcrumbSort.Newest
   );
 
+  const entryIndex = event.entries.findIndex(
+    entry => entry.type === EntryType.BREADCRUMBS
+  );
+
   const initialBreadcrumbs = useMemo(() => {
     let crumbs = data.values;
 
@@ -160,7 +165,7 @@ function BreadcrumbsContainer({data, event, organization, projectSlug, isShare}:
     return filterLevels;
   }
 
-  function applySearchTerm(breadcrumbs: Crumb[], newSearchTerm: string) {
+  function applySearchTerm(breadcrumbs: BreadcrumbWithMeta[], newSearchTerm: string) {
     if (!newSearchTerm.trim()) {
       return breadcrumbs;
     }
@@ -172,11 +177,11 @@ function BreadcrumbsContainer({data, event, organization, projectSlug, isShare}:
       .replace(/((^")|("$))/g, '')
       .toLocaleLowerCase();
 
-    return breadcrumbs.filter(obj =>
+    return breadcrumbs.filter(({breadcrumb}) =>
       Object.keys(
-        pick(obj, ['type', 'category', 'message', 'level', 'timestamp', 'data'])
+        pick(breadcrumb, ['type', 'category', 'message', 'level', 'timestamp', 'data'])
       ).some(key => {
-        const info = obj[key];
+        const info = breadcrumb[key];
 
         if (!defined(info) || !String(info).trim()) {
           return false;
@@ -192,7 +197,7 @@ function BreadcrumbsContainer({data, event, organization, projectSlug, isShare}:
   }
 
   function applySelectedFilters(
-    breadcrumbs: Crumb[],
+    breadcrumbs: BreadcrumbWithMeta[],
     selectedFilterOptions: SelectOption<string>[]
   ) {
     const checkedTypeOptions = new Set(
@@ -209,21 +214,21 @@ function BreadcrumbsContainer({data, event, organization, projectSlug, isShare}:
 
     if (!![...checkedTypeOptions].length && !![...checkedLevelOptions].length) {
       return breadcrumbs.filter(
-        filteredCrumb =>
-          checkedTypeOptions.has(filteredCrumb.type) &&
-          checkedLevelOptions.has(filteredCrumb.level)
+        ({breadcrumb}) =>
+          checkedTypeOptions.has(breadcrumb.type) &&
+          checkedLevelOptions.has(breadcrumb.level)
       );
     }
 
     if ([...checkedTypeOptions].length) {
-      return breadcrumbs.filter(filteredCrumb =>
-        checkedTypeOptions.has(filteredCrumb.type)
+      return breadcrumbs.filter(({breadcrumb}) =>
+        checkedTypeOptions.has(breadcrumb.type)
       );
     }
 
     if ([...checkedLevelOptions].length) {
-      return breadcrumbs.filter(filteredCrumb =>
-        checkedLevelOptions.has(filteredCrumb.level)
+      return breadcrumbs.filter(({breadcrumb}) =>
+        checkedLevelOptions.has(breadcrumb.level)
       );
     }
 
@@ -231,8 +236,12 @@ function BreadcrumbsContainer({data, event, organization, projectSlug, isShare}:
   }
 
   const displayedBreadcrumbs = useMemo(() => {
+    const breadcrumbsWithMeta = initialBreadcrumbs.map((breadcrumb, index) => ({
+      breadcrumb,
+      meta: event._meta?.entries?.[entryIndex]?.data?.values?.[index],
+    }));
     const filteredBreadcrumbs = applySearchTerm(
-      applySelectedFilters(initialBreadcrumbs, filterSelections),
+      applySelectedFilters(breadcrumbsWithMeta, filterSelections),
       searchTerm
     );
 
@@ -242,7 +251,14 @@ function BreadcrumbsContainer({data, event, organization, projectSlug, isShare}:
     return sort === BreadcrumbSort.Newest
       ? [...filteredBreadcrumbs].reverse()
       : filteredBreadcrumbs;
-  }, [filterSelections, initialBreadcrumbs, searchTerm, sort]);
+  }, [
+    entryIndex,
+    event._meta?.entries,
+    filterSelections,
+    initialBreadcrumbs,
+    searchTerm,
+    sort,
+  ]);
 
   function getEmptyMessage() {
     if (displayedBreadcrumbs.length) {

+ 8 - 0
static/app/components/events/interfaces/breadcrumbs/types.tsx

@@ -0,0 +1,8 @@
+import {Crumb} from 'sentry/types/breadcrumbs';
+
+export type BreadcrumbMeta = Record<string, any>;
+
+export type BreadcrumbWithMeta = {
+  breadcrumb: Crumb;
+  meta: BreadcrumbMeta;
+};