eventOrGroupTitle.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import {Fragment} from 'react';
  2. import {css} from '@emotion/react';
  3. import styled from '@emotion/styled';
  4. import ProjectsStore from 'sentry/stores/projectsStore';
  5. import {BaseGroup, GroupTombstone, IssueCategory, Organization} from 'sentry/types';
  6. import {Event} from 'sentry/types/event';
  7. import {getTitle} from 'sentry/utils/events';
  8. import withOrganization from 'sentry/utils/withOrganization';
  9. import EventTitleTreeLabel from './eventTitleTreeLabel';
  10. import {StackTracePreview} from './stacktracePreview';
  11. type Props = {
  12. data: Event | BaseGroup | GroupTombstone;
  13. organization: Organization;
  14. className?: string;
  15. /* is issue breakdown? */
  16. grouping?: boolean;
  17. hasGuideAnchor?: boolean;
  18. withStackTracePreview?: boolean;
  19. };
  20. function EventOrGroupTitle({
  21. organization,
  22. data,
  23. withStackTracePreview,
  24. grouping = false,
  25. className,
  26. }: Props) {
  27. const event = data as Event;
  28. const groupingCurrentLevel = (data as BaseGroup).metadata?.current_level;
  29. const groupingIssueCategory = (data as BaseGroup)?.issueCategory;
  30. const hasGroupingTreeUI = !!organization?.features.includes('grouping-tree-ui');
  31. const hasGroupingStacktraceUI = !!organization?.features.includes(
  32. 'grouping-stacktrace-ui'
  33. );
  34. const {id, eventID, groupID, projectID} = event;
  35. const {title, subtitle, treeLabel} = getTitle(event, organization?.features, grouping);
  36. return (
  37. <Wrapper className={className} hasGroupingTreeUI={hasGroupingTreeUI}>
  38. {withStackTracePreview && groupingIssueCategory === IssueCategory.ERROR ? (
  39. <StyledStacktracePreview
  40. organization={organization}
  41. issueId={groupID ? groupID : id}
  42. groupingCurrentLevel={groupingCurrentLevel}
  43. // we need eventId and projectSlug only when hovering over Event, not Group
  44. // (different API call is made to get the stack trace then)
  45. eventId={eventID}
  46. projectSlug={eventID ? ProjectsStore.getById(projectID)?.slug : undefined}
  47. hasGroupingStacktraceUI={hasGroupingStacktraceUI}
  48. >
  49. {treeLabel ? <EventTitleTreeLabel treeLabel={treeLabel} /> : title}
  50. </StyledStacktracePreview>
  51. ) : treeLabel ? (
  52. <EventTitleTreeLabel treeLabel={treeLabel} />
  53. ) : (
  54. title
  55. )}
  56. {subtitle && (
  57. <Fragment>
  58. <Spacer />
  59. <Subtitle title={subtitle}>{subtitle}</Subtitle>
  60. <br />
  61. </Fragment>
  62. )}
  63. </Wrapper>
  64. );
  65. }
  66. export default withOrganization(EventOrGroupTitle);
  67. /**
  68. * &nbsp; is used instead of margin/padding to split title and subtitle
  69. * into 2 separate text nodes on the HTML AST. This allows the
  70. * title to be highlighted without spilling over to the subtitle.
  71. */
  72. const Spacer = () => <span style={{display: 'inline-block', width: 10}}>&nbsp;</span>;
  73. const Subtitle = styled('em')`
  74. color: ${p => p.theme.gray300};
  75. font-style: normal;
  76. `;
  77. const StyledStacktracePreview = styled(StackTracePreview)<{
  78. hasGroupingStacktraceUI: boolean;
  79. }>`
  80. ${p =>
  81. p.hasGroupingStacktraceUI &&
  82. css`
  83. display: inline-flex;
  84. overflow: hidden;
  85. height: 100%;
  86. > span:first-child {
  87. ${p.theme.overflowEllipsis}
  88. }
  89. `}
  90. `;
  91. const Wrapper = styled('span')<{hasGroupingTreeUI: boolean}>`
  92. font-size: ${p => p.theme.fontSizeLarge};
  93. ${p =>
  94. p.hasGroupingTreeUI &&
  95. css`
  96. display: inline-grid;
  97. grid-template-columns: auto max-content 1fr max-content;
  98. align-items: baseline;
  99. ${Subtitle} {
  100. ${p.theme.overflowEllipsis};
  101. display: inline-block;
  102. height: 100%;
  103. }
  104. `}
  105. `;