Browse Source

ref(breadcrumb): Converted Breadcrumb to Typescript (#18117)

Priscila Oliveira 4 years ago
parent
commit
47a8a4d730

+ 1 - 1
src/sentry/static/sentry/app/components/events/eventEntries.jsx

@@ -6,7 +6,7 @@ import {analytics} from 'app/utils/analytics';
 import {logException} from 'app/utils/logging';
 import {objectIsEmpty} from 'app/utils';
 import {t} from 'app/locale';
-import BreadcrumbsInterface from 'app/components/events/interfaces/breadcrumbs';
+import BreadcrumbsInterface from 'app/components/events/interfaces/breadcrumbs/breadcrumbs';
 import CspInterface from 'app/components/events/interfaces/csp';
 import DebugMetaInterface from 'app/components/events/interfaces/debugmeta';
 import EventAttachments from 'app/components/events/eventAttachments';

+ 0 - 228
src/sentry/static/sentry/app/components/events/interfaces/breadcrumbs.jsx

@@ -1,228 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-
-import EventDataSection from 'app/components/events/eventDataSection';
-import SentryTypes from 'app/sentryTypes';
-import GuideAnchor from 'app/components/assistant/guideAnchor';
-import Breadcrumb from 'app/components/events/interfaces/breadcrumbs/breadcrumb';
-import {t, tct} from 'app/locale';
-import {PlatformContext} from 'app/components/events/interfaces/breadcrumbs/platformContext';
-
-function Collapsed(props) {
-  return (
-    <li className="crumbs-collapsed">
-      <a onClick={props.onClick}>
-        <span className="icon-container">
-          <span className="icon icon-ellipsis" />
-        </span>
-        {tct('Show [count] collapsed crumbs', {count: props.count})}
-      </a>
-    </li>
-  );
-}
-
-Collapsed.propTypes = {
-  onClick: PropTypes.func.isRequired,
-  count: PropTypes.number.isRequired,
-};
-
-function moduleToCategory(module) {
-  if (!module) {
-    return null;
-  }
-  const match = module.match(/^.*\/(.*?)(:\d+)/);
-  if (match) {
-    return match[1];
-  }
-  return module.split(/./)[0];
-}
-
-class BreadcrumbsInterface extends React.Component {
-  static propTypes = {
-    event: SentryTypes.Event.isRequired,
-    type: PropTypes.string.isRequired,
-    data: PropTypes.object.isRequired,
-  };
-
-  static contextTypes = {
-    organization: SentryTypes.Organization,
-    project: SentryTypes.Project,
-  };
-
-  constructor(...args) {
-    super(...args);
-    this.state = {
-      collapsed: true,
-      queryValue: '',
-    };
-  }
-
-  static MAX_CRUMBS_WHEN_COLLAPSED = 10;
-
-  onCollapseToggle = () => {
-    this.setState({
-      collapsed: !this.state.collapsed,
-    });
-  };
-
-  renderBreadcrumbs = crumbs =>
-    // reverse array to get consistent idx between collapsed/expanded state
-    // (indexes begin and increment from last breadcrumb)
-    crumbs
-      .reverse()
-      .map((item, idx) => <Breadcrumb key={idx} crumb={item} />)
-      .reverse(); // un-reverse rendered result
-
-  renderNoMatch = () => (
-    <li className="crumb-empty">
-      <p>
-        <span className="icon icon-exclamation" />{' '}
-        {t('Sorry, no breadcrumbs match your search query.')}
-      </p>
-    </li>
-  );
-
-  getVirtualCrumb = () => {
-    const evt = this.props.event;
-    let crumb;
-
-    const exception = evt.entries.find(entry => entry.type === 'exception');
-    if (exception) {
-      const {type, value, module} = exception.data.values[0];
-      crumb = {
-        type: 'error',
-        level: 'error',
-        category: moduleToCategory(module || null) || 'exception',
-        data: {
-          type,
-          value,
-        },
-      };
-    } else if (evt.message) {
-      const levelTag = (evt.tags || []).find(tag => tag.key === 'level');
-      const level = levelTag && levelTag.value;
-      crumb = {
-        type: 'message',
-        level,
-        category: 'message',
-        message: evt.message,
-      };
-    }
-
-    if (crumb) {
-      Object.assign(crumb, {
-        timestamp: evt.dateCreated,
-        last: true,
-      });
-    }
-
-    return crumb;
-  };
-
-  setQuery = evt => {
-    this.setState({
-      queryValue: evt.target.value,
-    });
-  };
-
-  filterCrumbs = (crumbs, queryValue) =>
-    crumbs.filter(
-      item =>
-        // return true if any of category, message, or level contain queryValue
-        !!['category', 'message', 'level'].find(prop => {
-          const propValue = (item[prop] || '').toLowerCase();
-          return propValue.includes(queryValue);
-        })
-    );
-
-  clearSearch = () => {
-    this.setState({
-      queryValue: '',
-      collapsed: true,
-    });
-  };
-
-  getSearchField = () => (
-    <div className="breadcrumb-filter">
-      <input
-        type="text"
-        className="search-input form-control"
-        placeholder={t('Search breadcrumbs...')}
-        autoComplete="off"
-        value={this.state.queryValue}
-        onChange={this.setQuery}
-      />
-      <span className="icon-search" />
-      {this.state.queryValue && (
-        <div>
-          <a className="search-clear-form" onClick={this.clearSearch}>
-            <span className="icon-circle-cross" />
-          </a>
-        </div>
-      )}
-    </div>
-  );
-
-  render() {
-    const evt = this.props.event;
-    const data = this.props.data;
-
-    const title = (
-      <h3>
-        <GuideAnchor target="breadcrumbs" position="bottom">
-          {t('Breadcrumbs')}
-        </GuideAnchor>
-      </h3>
-    );
-
-    let all = data.values;
-
-    // Add the error event as the final (virtual) breadcrumb
-    const virtualCrumb = this.getVirtualCrumb();
-    if (virtualCrumb) {
-      // make copy of values array / don't mutate props
-      all = all.slice(0).concat([virtualCrumb]);
-    }
-
-    // filter breadcrumbs on text input
-    const {queryValue} = this.state;
-    const filtered = queryValue ? this.filterCrumbs(all, queryValue.toLowerCase()) : all;
-
-    // cap max number of breadcrumbs to show
-    const MAX = BreadcrumbsInterface.MAX_CRUMBS_WHEN_COLLAPSED;
-    let crumbs = filtered;
-    if (this.state.collapsed && filtered.length > MAX) {
-      crumbs = filtered.slice(-MAX);
-    }
-
-    const numCollapsed = filtered.length - crumbs.length;
-
-    let crumbContent;
-    if (crumbs.length) {
-      crumbContent = this.renderBreadcrumbs(crumbs);
-    } else if (all.length) {
-      crumbContent = this.renderNoMatch();
-    }
-    return (
-      <EventDataSection
-        className="breadcrumb-box"
-        event={evt}
-        type={this.props.type}
-        title={title}
-        actions={this.getSearchField()}
-        wrapTitle={false}
-      >
-        <PlatformContext.Provider value={evt.platform}>
-          <ul className="crumbs">
-            {numCollapsed > 0 && (
-              <Collapsed onClick={this.onCollapseToggle} count={numCollapsed} />
-            )}
-            {crumbContent}
-          </ul>
-        </PlatformContext.Provider>
-      </EventDataSection>
-    );
-  }
-}
-
-export default BreadcrumbsInterface;

+ 0 - 96
src/sentry/static/sentry/app/components/events/interfaces/breadcrumbs/breadcrumb.jsx

@@ -1,96 +0,0 @@
-import PropTypes from 'prop-types';
-import React from 'react';
-import moment from 'moment';
-
-import {defined} from 'app/utils';
-import HttpRenderer from 'app/components/events/interfaces/breadcrumbs/httpRenderer';
-import ErrorRenderer from 'app/components/events/interfaces/breadcrumbs/errorRenderer';
-import DefaultRenderer from 'app/components/events/interfaces/breadcrumbs/defaultRenderer';
-import ErrorBoundary from 'app/components/errorBoundary';
-import Tooltip from 'app/components/tooltip';
-import getDynamicText from 'app/utils/getDynamicText';
-
-class Breadcrumb extends React.Component {
-  static propTypes = {
-    crumb: PropTypes.object.isRequired,
-  };
-
-  getClassName = () => {
-    const {crumb} = this.props;
-
-    // use Set to avoid duplicate crumb classes (was previously adding
-    // values like "crumb-default" as many as three times)
-    const classes = new Set(['crumb', 'crumb-default', 'crumb-' + crumb.level]);
-
-    if (crumb.type !== 'default') {
-      classes.add('crumb-' + crumb.type.replace(/[\s_]+/g, '-').toLowerCase());
-    }
-
-    // special case for 'ui.' and `sentry.` category breadcrumbs
-    // TODO: find a better way to customize UI around non-schema data
-    const isDotSeparatedCategory = /.+\..+/.test(crumb.category);
-    if (isDotSeparatedCategory) {
-      const [category, subcategory] = crumb.category.split('.');
-      if (category === 'ui') {
-        classes.add('crumb-user');
-      } else if (category === 'sentry' && subcategory === 'transaction') {
-        // Warning has a precedence over other icons, so we want to force it.
-        classes.delete('crumb-warning');
-        classes.add('crumb-navigation');
-      }
-    }
-
-    if (crumb.last) {
-      classes.add('crumb-last');
-    }
-
-    return [...classes].join(' ');
-  };
-
-  getTooltipTitle = () => {
-    const {crumb} = this.props;
-    const parsedTimestamp = moment(crumb.timestamp);
-    const timestampFormat = parsedTimestamp.milliseconds() ? 'll H:mm:ss.SSS A' : 'lll';
-    return parsedTimestamp.format(timestampFormat);
-  };
-
-  renderType = () => {
-    const {crumb} = this.props;
-    switch (crumb.type) {
-      case 'error':
-        return <ErrorRenderer crumb={crumb} />;
-      case 'http':
-        return <HttpRenderer crumb={crumb} />;
-      default:
-        return <DefaultRenderer crumb={crumb} />;
-    }
-  };
-
-  render() {
-    const {crumb} = this.props;
-    return (
-      <li className={this.getClassName()}>
-        <ErrorBoundary mini css={{margin: 0, borderRadius: 0}}>
-          <span className="icon-container">
-            <span className="icon" />
-          </span>
-          {defined(crumb.timestamp) ? (
-            <Tooltip title={this.getTooltipTitle()}>
-              <span className="dt">
-                {getDynamicText({
-                  value: moment(crumb.timestamp).format('HH:mm:ss'),
-                  fixed: '00:00:00',
-                })}
-              </span>
-            </Tooltip>
-          ) : (
-            <span className="dt" />
-          )}
-          {this.renderType()}
-        </ErrorBoundary>
-      </li>
-    );
-  }
-}
-
-export default Breadcrumb;

+ 31 - 0
src/sentry/static/sentry/app/components/events/interfaces/breadcrumbs/breadcrumbCollapsed.tsx

@@ -0,0 +1,31 @@
+import React from 'react';
+import styled from '@emotion/styled';
+
+import {tct} from 'app/locale';
+import {IconEllipsis} from 'app/icons/iconEllipsis';
+
+import {BreadCrumb, BreadCrumbIconWrapper} from './styles';
+
+type Props = {
+  onClick: () => void;
+  quantity: number;
+};
+
+const BreadcrumbCollapsed = ({quantity, onClick}: Props) => (
+  <StyledBreadCrumb data-test-id="breadcrumb-collapsed" onClick={onClick}>
+    <BreadCrumbIconWrapper>
+      <IconEllipsis />
+    </BreadCrumbIconWrapper>
+    {tct('Show [quantity] collapsed crumbs', {quantity})}
+  </StyledBreadCrumb>
+);
+
+export default BreadcrumbCollapsed;
+
+const StyledBreadCrumb = styled(BreadCrumb)`
+  cursor: pointer;
+  background: ${p => p.theme.whiteDark};
+  margin: 0 -1px;
+  border-right: 1px solid ${p => p.theme.borderLight};
+  border-left: 1px solid ${p => p.theme.borderLight};
+`;

+ 36 - 0
src/sentry/static/sentry/app/components/events/interfaces/breadcrumbs/breadcrumbTime.tsx

@@ -0,0 +1,36 @@
+import React from 'react';
+import styled from '@emotion/styled';
+import moment from 'moment';
+
+import {defined} from 'app/utils';
+import Tooltip from 'app/components/tooltip';
+import getDynamicText from 'app/utils/getDynamicText';
+
+const getBreadcrumbTimeTooltipTitle = (timestamp: string) => {
+  const parsedTimestamp = moment(timestamp);
+  const timestampFormat = parsedTimestamp.milliseconds() ? 'll H:mm:ss.SSS A' : 'lll';
+  return parsedTimestamp.format(timestampFormat);
+};
+
+type Props = {
+  timestamp?: string;
+};
+
+const BreadcrumbTime = ({timestamp}: Props) =>
+  defined(timestamp) ? (
+    <Tooltip title={getBreadcrumbTimeTooltipTitle(timestamp)}>
+      <Time>
+        {getDynamicText({
+          value: moment(timestamp).format('HH:mm:ss'),
+          fixed: '00:00:00',
+        })}
+      </Time>
+    </Tooltip>
+  ) : null;
+
+export default BreadcrumbTime;
+
+const Time = styled('div')`
+  font-size: ${p => p.theme.fontSizeSmall};
+  color: ${p => p.theme.gray4};
+`;

+ 247 - 0
src/sentry/static/sentry/app/components/events/interfaces/breadcrumbs/breadcrumbs.tsx

@@ -0,0 +1,247 @@
+import React from 'react';
+import styled from '@emotion/styled';
+
+import EventDataSection from 'app/components/events/eventDataSection';
+import GuideAnchor from 'app/components/assistant/guideAnchor';
+import EmptyStateWarning from 'app/components/emptyStateWarning';
+import {t} from 'app/locale';
+import {Event} from 'app/types';
+import space from 'app/styles/space';
+
+import {PlatformContextProvider} from './platformContext';
+import BreadCrumbsSearch from './breadcrumbsSearch';
+import BreadcrumbTime from './breadcrumbTime';
+import BreadcrumbCollapsed from './breadcrumbCollapsed';
+import convertBreadcrumbType from './convertBreadcrumbType';
+import getBreadcrumbDetails from './getBreadcrumbDetails';
+import {Breadcrumb} from './types';
+import {BreadCrumb, BreadCrumbIconWrapper} from './styles';
+
+const MAX_CRUMBS_WHEN_COLLAPSED = 10;
+
+type State = {
+  isCollapsed: boolean;
+  searchTerm: string;
+  breadcrumbs: Array<Breadcrumb>;
+  filteredBreadcrumbs: Array<Breadcrumb>;
+};
+
+type Props = {
+  event: Event;
+  type: string;
+  data: {
+    values: Array<Breadcrumb>;
+  };
+};
+
+class BreadcrumbsContainer extends React.Component<Props, State> {
+  state: State = {
+    isCollapsed: true,
+    searchTerm: '',
+    breadcrumbs: [],
+    filteredBreadcrumbs: [],
+  };
+
+  componentDidMount() {
+    this.loadCrumbs();
+  }
+
+  loadCrumbs = () => {
+    const {data} = this.props;
+    let breadcrumbs = data.values;
+
+    // Add the error event as the final (virtual) breadcrumb
+    const virtualCrumb = this.getVirtualCrumb();
+    if (virtualCrumb) {
+      breadcrumbs = [...data.values, virtualCrumb];
+    }
+
+    this.setState({
+      breadcrumbs,
+      filteredBreadcrumbs: breadcrumbs,
+    });
+  };
+
+  moduleToCategory = (module: any) => {
+    if (!module) {
+      return undefined;
+    }
+    const match = module.match(/^.*\/(.*?)(:\d+)/);
+    if (!match) {
+      return module.split(/./)[0];
+    }
+    return match[1];
+  };
+
+  getVirtualCrumb = (): Breadcrumb | undefined => {
+    const {event} = this.props;
+
+    const exception = event.entries.find(entry => entry.type === 'exception');
+
+    if (!exception && !event.message) {
+      return undefined;
+    }
+
+    if (exception) {
+      const {type, value, module: mdl} = exception.data.values[0];
+      return {
+        type: 'error',
+        level: 'error',
+        category: this.moduleToCategory(mdl) || 'exception',
+        data: {
+          type,
+          value,
+        },
+        timestamp: event.dateCreated,
+      };
+    }
+
+    const levelTag = (event.tags || []).find(tag => tag.key === 'level');
+
+    return {
+      type: 'message',
+      level: levelTag?.value as Breadcrumb['level'],
+      category: 'message',
+      message: event.message,
+      timestamp: event.dateCreated,
+    };
+  };
+
+  getCollapsedCrumbQuantity = (): {
+    filteredCollapsedBreadcrumbs: Array<Breadcrumb>;
+    collapsedQuantity: number;
+  } => {
+    const {isCollapsed, filteredBreadcrumbs} = this.state;
+
+    let filteredCollapsedBreadcrumbs = filteredBreadcrumbs;
+
+    if (isCollapsed && filteredCollapsedBreadcrumbs.length > MAX_CRUMBS_WHEN_COLLAPSED) {
+      filteredCollapsedBreadcrumbs = filteredCollapsedBreadcrumbs.slice(
+        -MAX_CRUMBS_WHEN_COLLAPSED
+      );
+    }
+
+    return {
+      filteredCollapsedBreadcrumbs,
+      collapsedQuantity: filteredBreadcrumbs.length - filteredCollapsedBreadcrumbs.length,
+    };
+  };
+
+  handleChangeSearchTerm = (searchTerm: string) => {
+    const {breadcrumbs} = this.state;
+
+    const filteredBreadcrumbs = breadcrumbs.filter(
+      item =>
+        // return true if any of category, message, or level contain queryValue
+        !!['category', 'message', 'level'].find(prop => {
+          const propValue = (item[prop] || '').toLowerCase();
+          return propValue.includes(searchTerm);
+        })
+    );
+
+    this.setState({
+      searchTerm,
+      filteredBreadcrumbs,
+    });
+  };
+
+  handleCollapseToggle = () => {
+    this.setState(prevState => ({
+      isCollapsed: !prevState.isCollapsed,
+    }));
+  };
+
+  handleCleanSearch = () => {
+    this.setState({
+      searchTerm: '',
+      isCollapsed: true,
+    });
+  };
+
+  render() {
+    const {event, type} = this.props;
+    const {searchTerm} = this.state;
+    const {
+      collapsedQuantity,
+      filteredCollapsedBreadcrumbs,
+    } = this.getCollapsedCrumbQuantity();
+
+    return (
+      <EventDataSection
+        type={type}
+        title={
+          <h3>
+            <GuideAnchor target="breadcrumbs" position="bottom">
+              {t('Breadcrumbs')}
+            </GuideAnchor>
+          </h3>
+        }
+        actions={
+          <BreadCrumbsSearch
+            searchTerm={searchTerm}
+            onChangeSearchTerm={this.handleChangeSearchTerm}
+            onClearSearchTerm={this.handleCleanSearch}
+          />
+        }
+        wrapTitle={false}
+      >
+        <Content>
+          {filteredCollapsedBreadcrumbs.length > 0 ? (
+            <PlatformContextProvider value={{platform: event.platform}}>
+              <BreadCrumbs className="crumbs">
+                {collapsedQuantity > 0 && (
+                  <BreadcrumbCollapsed
+                    onClick={this.handleCollapseToggle}
+                    quantity={collapsedQuantity}
+                  />
+                )}
+                {filteredCollapsedBreadcrumbs.map((crumb, idx) => {
+                  const convertedBreadcrumb = convertBreadcrumbType(crumb);
+                  const {color, borderColor, icon, renderer} = getBreadcrumbDetails(
+                    convertedBreadcrumb
+                  );
+
+                  return (
+                    <BreadCrumb
+                      data-test-id="breadcrumb"
+                      key={idx}
+                      error={
+                        convertedBreadcrumb.type === 'message' ||
+                        convertedBreadcrumb.type === 'error'
+                      }
+                    >
+                      <BreadCrumbIconWrapper color={color} borderColor={borderColor}>
+                        {icon}
+                      </BreadCrumbIconWrapper>
+                      {renderer}
+                      <BreadcrumbTime timestamp={crumb.timestamp} />
+                    </BreadCrumb>
+                  );
+                })}
+              </BreadCrumbs>
+            </PlatformContextProvider>
+          ) : (
+            <EmptyStateWarning small>
+              {t('Sorry, no breadcrumbs match your search query.')}
+            </EmptyStateWarning>
+          )}
+        </Content>
+      </EventDataSection>
+    );
+  }
+}
+
+export default BreadcrumbsContainer;
+
+const BreadCrumbs = styled('ul')`
+  padding-left: 0;
+  list-style: none;
+  margin-bottom: 0;
+`;
+
+const Content = styled('div')`
+  border: 1px solid ${p => p.theme.borderLight};
+  border-radius: 3px;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
+  margin-bottom: ${space(3)};
+`;

+ 68 - 0
src/sentry/static/sentry/app/components/events/interfaces/breadcrumbs/breadcrumbsSearch.tsx

@@ -0,0 +1,68 @@
+import React from 'react';
+import styled from '@emotion/styled';
+
+import TextField from 'app/components/forms/textField';
+import {IconSearch} from 'app/icons/iconSearch';
+import {t} from 'app/locale';
+import {IconClose} from 'app/icons/iconClose';
+import space from 'app/styles/space';
+
+type Props = {
+  searchTerm: string;
+  onChangeSearchTerm: TextField['props']['onChange'];
+  onClearSearchTerm: () => void;
+};
+
+const BreadCrumbsSearch = ({
+  searchTerm,
+  onChangeSearchTerm,
+  onClearSearchTerm,
+}: Props) => (
+  <Wrapper data-test-id="breadcumber-search">
+    <StyledTextField
+      name="breadcumber-search"
+      placeholder={t('Search breadcrumbs...')}
+      autoComplete="off"
+      value={searchTerm}
+      onChange={onChangeSearchTerm}
+    />
+    <StyledIconSearch />
+    <StyledIconClose show={!!searchTerm} onClick={onClearSearchTerm} circle />
+  </Wrapper>
+);
+
+export default BreadCrumbsSearch;
+
+const Wrapper = styled('div')`
+  position: relative;
+  display: flex;
+  align-items: center;
+`;
+
+const StyledTextField = styled(TextField)<TextField['props']>`
+  margin-bottom: 0;
+  input {
+    padding-left: ${space(4)};
+    padding-right: ${space(4)};
+    height: 28px;
+  }
+`;
+
+const StyledIconSearch = styled(IconSearch)`
+  position: absolute;
+  color: ${p => p.theme.gray2};
+  font-size: ${p => p.theme.fontSizeMedium};
+  left: ${space(1)};
+`;
+
+const StyledIconClose = styled(IconClose, {
+  shouldForwardProp: p => p !== 'show',
+})<{
+  show: boolean;
+}>`
+  position: absolute;
+  cursor: pointer;
+  color: ${p => p.theme.gray6};
+  right: ${space(0.75)};
+  visibility: ${p => (p.show ? 'visible' : 'hidden')};
+`;

+ 51 - 0
src/sentry/static/sentry/app/components/events/interfaces/breadcrumbs/convertBreadcrumbType.tsx

@@ -0,0 +1,51 @@
+import {Breadcrumb} from './types';
+
+function convertBreadcrumbType(breadcrumb: Breadcrumb): Breadcrumb {
+  if (breadcrumb.level) {
+    if (breadcrumb.level === 'warning') {
+      return {
+        ...breadcrumb,
+        type: 'warning',
+      };
+    }
+
+    if (breadcrumb.level === 'error') {
+      return {
+        ...breadcrumb,
+        type: 'error',
+      };
+    }
+  }
+  // special case for 'ui.' and `sentry.` category breadcrumbs
+  // TODO: find a better way to customize UI around non-schema data
+  if ((!breadcrumb.type || breadcrumb.type === 'default') && breadcrumb.category) {
+    const [category, subcategory] = breadcrumb.category.split('.');
+    if (category === 'ui') {
+      return {
+        ...breadcrumb,
+        type: 'ui',
+      };
+    }
+
+    if (category === 'console' || category === 'navigation') {
+      return {
+        ...breadcrumb,
+        type: 'debug',
+      };
+    }
+
+    if (
+      category === 'sentry' &&
+      (subcategory === 'transaction' || subcategory === 'event')
+    ) {
+      return {
+        ...breadcrumb,
+        type: 'error',
+      };
+    }
+  }
+
+  return breadcrumb;
+}
+
+export default convertBreadcrumbType;

+ 5 - 5
src/sentry/static/sentry/app/components/events/interfaces/breadcrumbs/crumbTable.tsx

@@ -3,13 +3,13 @@ import React from 'react';
 import Category from 'app/components/events/interfaces/breadcrumbs/category';
 import {getMeta} from 'app/components/events/meta/metaProxy';
 
-import {Crumb} from './types';
+import {Breadcrumb} from './types';
 import getBreadcrumbCustomRendererValue from './getBreadcrumbCustomRendererValue';
 
 type Props = {
-  crumb: Crumb;
+  breadcrumb: Breadcrumb;
   kvData?: KvData;
-  summary: React.ReactElement;
+  summary: React.ReactNode;
   children?: React.ReactNode;
 };
 
@@ -17,7 +17,7 @@ type KvData = {
   [key: string]: any;
 };
 
-const CrumbTable = ({children, kvData, crumb, summary}: Props) => {
+const CrumbTable = ({children, kvData, breadcrumb, summary}: Props) => {
   const renderData = () => {
     if (!kvData) {
       return null;
@@ -45,7 +45,7 @@ const CrumbTable = ({children, kvData, crumb, summary}: Props) => {
       <thead>
         <tr>
           <td className="key">
-            <Category value={crumb.category} />
+            <Category value={breadcrumb.category} />
           </td>
           <td className="value">{summary}</td>
         </tr>

+ 8 - 9
src/sentry/static/sentry/app/components/events/interfaces/breadcrumbs/defaultRenderer.tsx

@@ -3,33 +3,32 @@ import React from 'react';
 import CrumbTable from 'app/components/events/interfaces/breadcrumbs/crumbTable';
 import SummaryLine from 'app/components/events/interfaces/breadcrumbs/summaryLine';
 import {getMeta} from 'app/components/events/meta/metaProxy';
-import {defined} from 'app/utils';
 
 import getBreadcrumbCustomRendererValue from './getBreadcrumbCustomRendererValue';
-import {Crumb} from './types';
+import {BreadcrumbTypeDefault, BreadcrumbTypeNavigation} from './types';
 
 type Props = {
-  crumb: Crumb;
+  breadcrumb: BreadcrumbTypeDefault | BreadcrumbTypeNavigation;
 };
 
-const DefaultRenderer = ({crumb}: Props) => (
+const DefaultRenderer = ({breadcrumb}: Props) => (
   <CrumbTable
-    crumb={crumb}
+    breadcrumb={breadcrumb}
     summary={
       <SummaryLine>
-        {defined(crumb?.message) && (
+        {breadcrumb?.message && (
           <pre>
             <code>
               {getBreadcrumbCustomRendererValue({
-                value: crumb.message,
-                meta: getMeta(crumb, 'message'),
+                value: breadcrumb.message,
+                meta: getMeta(breadcrumb, 'message'),
               })}
             </code>
           </pre>
         )}
       </SummaryLine>
     }
-    kvData={crumb.data}
+    kvData={breadcrumb.data}
   />
 );
 

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