eventDetailsHeader.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. import styled from '@emotion/styled';
  2. import {Button} from 'sentry/components/button';
  3. import {Flex} from 'sentry/components/container/flex';
  4. import ErrorBoundary from 'sentry/components/errorBoundary';
  5. import {DatePageFilter} from 'sentry/components/organizations/datePageFilter';
  6. import {EnvironmentPageFilter} from 'sentry/components/organizations/environmentPageFilter';
  7. import {IconChevron} from 'sentry/icons/iconChevron';
  8. import {t} from 'sentry/locale';
  9. import {space} from 'sentry/styles/space';
  10. import type {Event} from 'sentry/types/event';
  11. import type {Group} from 'sentry/types/group';
  12. import {useLocation} from 'sentry/utils/useLocation';
  13. import {useNavigate} from 'sentry/utils/useNavigate';
  14. import {useSyncedLocalStorageState} from 'sentry/utils/useSyncedLocalStorageState';
  15. import {
  16. EventSearch,
  17. useEventQuery,
  18. } from 'sentry/views/issueDetails/streamline/eventSearch';
  19. import {useEnvironmentsFromUrl} from 'sentry/views/issueDetails/utils';
  20. import {EventGraph} from './eventGraph';
  21. export function EventDetailsHeader({
  22. group,
  23. event,
  24. }: {
  25. event: Event | undefined;
  26. group: Group;
  27. }) {
  28. const navigate = useNavigate();
  29. const location = useLocation();
  30. const environments = useEnvironmentsFromUrl();
  31. const searchQuery = useEventQuery({group});
  32. const [sidebarOpen, setSidebarOpen] = useSyncedLocalStorageState(
  33. 'issue-details-sidebar-open',
  34. true
  35. );
  36. const direction = sidebarOpen ? 'right' : 'left';
  37. return (
  38. <PageErrorBoundary mini message={t('There was an error loading the event filters')}>
  39. <FilterContainer>
  40. <EnvironmentFilter
  41. triggerProps={{
  42. borderless: true,
  43. style: {
  44. borderRadius: 0,
  45. },
  46. }}
  47. />
  48. <DateFilter
  49. triggerProps={{
  50. borderless: true,
  51. style: {
  52. borderRadius: 0,
  53. },
  54. }}
  55. />
  56. <Flex style={{gridArea: 'search'}}>
  57. <SearchFilter
  58. group={group}
  59. handleSearch={query => {
  60. navigate({...location, query: {...location.query, query}}, {replace: true});
  61. }}
  62. environments={environments}
  63. query={searchQuery}
  64. queryBuilderProps={{
  65. disallowFreeText: true,
  66. }}
  67. />
  68. <ToggleContainer sidebarOpen={sidebarOpen}>
  69. <ToggleButton
  70. onClick={() => setSidebarOpen(!sidebarOpen)}
  71. aria-label={sidebarOpen ? t('Close Sidebar') : t('Open Sidebar')}
  72. >
  73. <LeftChevron direction={direction} />
  74. <RightChevron direction={direction} />
  75. </ToggleButton>
  76. </ToggleContainer>
  77. </Flex>
  78. <Graph event={event} group={group} />
  79. </FilterContainer>
  80. </PageErrorBoundary>
  81. );
  82. }
  83. const FilterContainer = styled('div')`
  84. padding-left: 24px;
  85. display: grid;
  86. grid-template-columns: auto auto minmax(100px, 1fr);
  87. grid-template-rows: minmax(38px, auto) auto;
  88. grid-template-areas:
  89. 'env date search toggle'
  90. 'graph graph graph graph';
  91. border: 0px solid ${p => p.theme.translucentBorder};
  92. border-width: 0 1px 1px 0;
  93. `;
  94. const EnvironmentFilter = styled(EnvironmentPageFilter)`
  95. grid-area: env;
  96. &:before {
  97. right: 0;
  98. top: ${space(1)};
  99. bottom: ${space(1)};
  100. width: 1px;
  101. content: '';
  102. position: absolute;
  103. background: ${p => p.theme.translucentInnerBorder};
  104. }
  105. `;
  106. const SearchFilter = styled(EventSearch)`
  107. border: 0;
  108. border-radius: 0;
  109. box-shadow: none;
  110. `;
  111. const DateFilter = styled(DatePageFilter)`
  112. grid-area: date;
  113. &:before {
  114. right: 0;
  115. top: ${space(1)};
  116. bottom: ${space(1)};
  117. width: 1px;
  118. content: '';
  119. position: absolute;
  120. background: ${p => p.theme.translucentInnerBorder};
  121. }
  122. `;
  123. const ToggleContainer = styled('div')<{sidebarOpen: boolean}>`
  124. width: ${p => (p.sidebarOpen ? '30px' : '50px')};
  125. position: relative;
  126. padding: ${space(0.5)} 0;
  127. @media (max-width: ${p => p.theme.breakpoints.large}) {
  128. display: none;
  129. }
  130. `;
  131. // The extra 1px on width is to display above the sidebar border
  132. const ToggleButton = styled(Button)`
  133. border-radius: ${p => p.theme.borderRadiusLeft};
  134. border-right-color: ${p => p.theme.background} !important;
  135. box-shadow: none;
  136. position: absolute;
  137. padding: 0;
  138. left: ${space(0.5)};
  139. width: calc(100% - ${space(0.5)} + 1px);
  140. outline: 0;
  141. height: 30px;
  142. min-height: unset;
  143. `;
  144. const LeftChevron = styled(IconChevron)`
  145. position: absolute;
  146. color: ${p => p.theme.subText};
  147. height: 10px;
  148. width: 10px;
  149. left: ${space(0.75)};
  150. `;
  151. const RightChevron = styled(LeftChevron)`
  152. left: ${space(1.5)};
  153. `;
  154. const Graph = styled(EventGraph)`
  155. border-top: 1px solid ${p => p.theme.translucentBorder};
  156. grid-area: graph;
  157. `;
  158. const PageErrorBoundary = styled(ErrorBoundary)`
  159. margin: 0;
  160. border: 0px solid ${p => p.theme.translucentBorder};
  161. border-width: 0 1px 1px 0;
  162. border-radius: 0;
  163. padding: ${space(1.5)} 24px;
  164. `;