eventOrGroupTitle.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  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, 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 hasGroupingTreeUI = !!organization?.features.includes('grouping-tree-ui');
  30. const hasGroupingStacktraceUI = !!organization?.features.includes(
  31. 'grouping-stacktrace-ui'
  32. );
  33. const {id, eventID, groupID, projectID} = event;
  34. const {title, subtitle, treeLabel} = getTitle(event, organization?.features, grouping);
  35. return (
  36. <Wrapper className={className} hasGroupingTreeUI={hasGroupingTreeUI}>
  37. {withStackTracePreview ? (
  38. <StyledStacktracePreview
  39. organization={organization}
  40. issueId={groupID ? groupID : id}
  41. groupingCurrentLevel={groupingCurrentLevel}
  42. // we need eventId and projectSlug only when hovering over Event, not Group
  43. // (different API call is made to get the stack trace then)
  44. eventId={eventID}
  45. projectSlug={eventID ? ProjectsStore.getById(projectID)?.slug : undefined}
  46. hasGroupingStacktraceUI={hasGroupingStacktraceUI}
  47. >
  48. {treeLabel ? <EventTitleTreeLabel treeLabel={treeLabel} /> : title}
  49. </StyledStacktracePreview>
  50. ) : treeLabel ? (
  51. <EventTitleTreeLabel treeLabel={treeLabel} />
  52. ) : (
  53. title
  54. )}
  55. {subtitle && (
  56. <Fragment>
  57. <Spacer />
  58. <Subtitle title={subtitle}>{subtitle}</Subtitle>
  59. <br />
  60. </Fragment>
  61. )}
  62. </Wrapper>
  63. );
  64. }
  65. export default withOrganization(EventOrGroupTitle);
  66. /**
  67. * &nbsp; is used instead of margin/padding to split title and subtitle
  68. * into 2 separate text nodes on the HTML AST. This allows the
  69. * title to be highlighted without spilling over to the subtitle.
  70. */
  71. const Spacer = () => <span style={{display: 'inline-block', width: 10}}>&nbsp;</span>;
  72. const Subtitle = styled('em')`
  73. color: ${p => p.theme.gray300};
  74. font-style: normal;
  75. `;
  76. const StyledStacktracePreview = styled(StackTracePreview)<{
  77. hasGroupingStacktraceUI: boolean;
  78. }>`
  79. ${p =>
  80. p.hasGroupingStacktraceUI &&
  81. css`
  82. display: inline-flex;
  83. overflow: hidden;
  84. height: 100%;
  85. > span:first-child {
  86. ${p.theme.overflowEllipsis}
  87. }
  88. `}
  89. `;
  90. const Wrapper = styled('span')<{hasGroupingTreeUI: boolean}>`
  91. ${p =>
  92. p.hasGroupingTreeUI &&
  93. css`
  94. display: inline-grid;
  95. grid-template-columns: auto max-content 1fr max-content;
  96. align-items: flex-end;
  97. line-height: 100%;
  98. ${Subtitle} {
  99. ${p.theme.overflowEllipsis};
  100. display: inline-block;
  101. height: 100%;
  102. }
  103. `}
  104. `;