eventOrGroupTitle.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import GuideAnchor from 'app/components/assistant/guideAnchor';
  4. import ProjectsStore from 'app/stores/projectsStore';
  5. import overflowEllipsis from 'app/styles/overflowEllipsis';
  6. import {BaseGroup, GroupTombstone, Organization} from 'app/types';
  7. import {Event} from 'app/types/event';
  8. import {getTitle} from 'app/utils/events';
  9. import withOrganization from 'app/utils/withOrganization';
  10. import EventTitleTreeLabel from './eventTitleTreeLabel';
  11. import StacktracePreview from './stacktracePreview';
  12. type Props = Partial<DefaultProps> & {
  13. data: Event | BaseGroup | GroupTombstone;
  14. organization: Organization;
  15. hasGuideAnchor?: boolean;
  16. withStackTracePreview?: boolean;
  17. guideAnchorName?: string;
  18. className?: string;
  19. };
  20. type DefaultProps = {
  21. guideAnchorName: string;
  22. };
  23. function EventOrGroupTitle({
  24. guideAnchorName = 'issue_title',
  25. organization,
  26. data,
  27. withStackTracePreview,
  28. hasGuideAnchor,
  29. className,
  30. }: Props) {
  31. const event = data as Event;
  32. const groupingCurrentLevel = (data as BaseGroup).metadata?.current_level;
  33. const hasGroupingTreeUI = !!organization?.features.includes('grouping-tree-ui');
  34. const hasGroupingStacktraceUI = !!organization?.features.includes(
  35. 'grouping-stacktrace-ui'
  36. );
  37. const {id, eventID, groupID, projectID} = event;
  38. const {title, subtitle, treeLabel} = getTitle(event, organization?.features);
  39. return (
  40. <Wrapper className={className} hasGroupingTreeUI={hasGroupingTreeUI}>
  41. <GuideAnchor disabled={!hasGuideAnchor} target={guideAnchorName} position="bottom">
  42. <StyledStacktracePreview
  43. organization={organization}
  44. issueId={groupID ? groupID : id}
  45. groupingCurrentLevel={groupingCurrentLevel}
  46. // we need eventId and projectSlug only when hovering over Event, not Group
  47. // (different API call is made to get the stack trace then)
  48. eventId={eventID}
  49. projectSlug={eventID ? ProjectsStore.getById(projectID)?.slug : undefined}
  50. disablePreview={!withStackTracePreview}
  51. hasGroupingStacktraceUI={hasGroupingStacktraceUI}
  52. >
  53. {treeLabel ? <EventTitleTreeLabel treeLabel={treeLabel} /> : title}
  54. </StyledStacktracePreview>
  55. </GuideAnchor>
  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. `
  83. display: inline-flex;
  84. overflow: hidden;
  85. > span:first-child {
  86. ${overflowEllipsis}
  87. }
  88. `}
  89. `;
  90. const Wrapper = styled('span')<{hasGroupingTreeUI: boolean}>`
  91. ${p =>
  92. p.hasGroupingTreeUI &&
  93. `
  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. ${overflowEllipsis};
  100. display: inline-block;
  101. }
  102. `}
  103. `;