index.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. import {useEffect, useMemo, useRef, useState} from 'react';
  2. import type {ListRowProps} from 'react-virtualized';
  3. import {AutoSizer, CellMeasurer, List as ReactVirtualizedList} from 'react-virtualized';
  4. import Placeholder from 'sentry/components/placeholder';
  5. import JumpButtons from 'sentry/components/replays/jumpButtons';
  6. import {useReplayContext} from 'sentry/components/replays/replayContext';
  7. import useJumpButtons from 'sentry/components/replays/useJumpButtons';
  8. import {t} from 'sentry/locale';
  9. import useCrumbHandlers from 'sentry/utils/replays/hooks/useCrumbHandlers';
  10. import useExtractedDomNodes from 'sentry/utils/replays/hooks/useExtractedDomNodes';
  11. import useOrganization from 'sentry/utils/useOrganization';
  12. import useVirtualizedInspector from 'sentry/views/replays/detail//useVirtualizedInspector';
  13. import BreadcrumbFilters from 'sentry/views/replays/detail/breadcrumbs/breadcrumbFilters';
  14. import BreadcrumbRow from 'sentry/views/replays/detail/breadcrumbs/breadcrumbRow';
  15. import useBreadcrumbFilters from 'sentry/views/replays/detail/breadcrumbs/useBreadcrumbFilters';
  16. import useScrollToCurrentItem from 'sentry/views/replays/detail/breadcrumbs/useScrollToCurrentItem';
  17. import FilterLoadingIndicator from 'sentry/views/replays/detail/filterLoadingIndicator';
  18. import FluidHeight from 'sentry/views/replays/detail/layout/fluidHeight';
  19. import NoRowRenderer from 'sentry/views/replays/detail/noRowRenderer';
  20. import useReplayPerfData from 'sentry/views/replays/detail/perfTable/useReplayPerfData';
  21. import TabItemContainer from 'sentry/views/replays/detail/tabItemContainer';
  22. import useVirtualizedList from 'sentry/views/replays/detail/useVirtualizedList';
  23. import useVirtualListDimentionChange from 'sentry/views/replays/detail/useVirtualListDimentionChange';
  24. // Ensure this object is created once as it is an input to
  25. // `useVirtualizedList`'s memoization
  26. const cellMeasurer = {
  27. fixedWidth: true,
  28. minHeight: 53,
  29. };
  30. function Breadcrumbs() {
  31. const {currentTime, replay} = useReplayContext();
  32. const organization = useOrganization();
  33. const hasPerfTab = organization.features.includes('session-replay-trace-table');
  34. const {onClickTimestamp} = useCrumbHandlers();
  35. const {data: frameToExtraction, isFetching: isFetchingExtractions} =
  36. useExtractedDomNodes({replay});
  37. const {data: frameToTrace, isFetching: isFetchingTraces} = useReplayPerfData({replay});
  38. const startTimestampMs = replay?.getStartTimestampMs() ?? 0;
  39. const frames = replay?.getChapterFrames();
  40. const [scrollToRow, setScrollToRow] = useState<undefined | number>(undefined);
  41. const filterProps = useBreadcrumbFilters({frames: frames || []});
  42. const {expandPathsRef, items, searchTerm, setSearchTerm} = filterProps;
  43. const clearSearchTerm = () => setSearchTerm('');
  44. const listRef = useRef<ReactVirtualizedList>(null);
  45. const deps = useMemo(() => [items, searchTerm], [items, searchTerm]);
  46. const {cache, updateList} = useVirtualizedList({
  47. cellMeasurer,
  48. ref: listRef,
  49. deps,
  50. });
  51. const {handleDimensionChange} = useVirtualListDimentionChange({cache, listRef});
  52. const {handleDimensionChange: handleInspectorExpanded} = useVirtualizedInspector({
  53. cache,
  54. listRef,
  55. expandPathsRef,
  56. });
  57. const {
  58. handleClick: onClickToJump,
  59. onRowsRendered,
  60. showJumpDownButton,
  61. showJumpUpButton,
  62. } = useJumpButtons({
  63. currentTime,
  64. frames: items,
  65. isTable: false,
  66. setScrollToRow,
  67. });
  68. useScrollToCurrentItem({
  69. frames,
  70. ref: listRef,
  71. });
  72. // Need to refresh the item dimensions as DOM & Trace data gets loaded
  73. useEffect(() => {
  74. if (!isFetchingExtractions || !isFetchingTraces) {
  75. updateList();
  76. }
  77. }, [isFetchingExtractions, isFetchingTraces, updateList]);
  78. const renderRow = ({index, key, style, parent}: ListRowProps) => {
  79. const item = (items || [])[index];
  80. return (
  81. <CellMeasurer
  82. cache={cache}
  83. columnIndex={0}
  84. key={key}
  85. parent={parent}
  86. rowIndex={index}
  87. >
  88. <BreadcrumbRow
  89. index={index}
  90. frame={item}
  91. extraction={frameToExtraction?.get(item)}
  92. traces={hasPerfTab ? frameToTrace?.get(item) : undefined}
  93. startTimestampMs={startTimestampMs}
  94. style={style}
  95. expandPaths={Array.from(expandPathsRef.current?.get(index) || [])}
  96. onClick={() => {
  97. onClickTimestamp(item);
  98. }}
  99. onDimensionChange={handleDimensionChange}
  100. onInspectorExpanded={handleInspectorExpanded}
  101. />
  102. </CellMeasurer>
  103. );
  104. };
  105. return (
  106. <FluidHeight>
  107. <FilterLoadingIndicator isLoading={isFetchingExtractions || isFetchingTraces}>
  108. <BreadcrumbFilters frames={frames} {...filterProps} />
  109. </FilterLoadingIndicator>
  110. <TabItemContainer data-test-id="replay-details-breadcrumbs-tab">
  111. {frames ? (
  112. <AutoSizer onResize={updateList}>
  113. {({height, width}) => (
  114. <ReactVirtualizedList
  115. deferredMeasurementCache={cache}
  116. height={height}
  117. noRowsRenderer={() => (
  118. <NoRowRenderer
  119. unfilteredItems={frames}
  120. clearSearchTerm={clearSearchTerm}
  121. >
  122. {t('No breadcrumbs recorded')}
  123. </NoRowRenderer>
  124. )}
  125. onRowsRendered={onRowsRendered}
  126. onScroll={() => {
  127. if (scrollToRow !== undefined) {
  128. setScrollToRow(undefined);
  129. }
  130. }}
  131. overscanRowCount={5}
  132. ref={listRef}
  133. rowCount={items.length}
  134. rowHeight={cache.rowHeight}
  135. rowRenderer={renderRow}
  136. scrollToIndex={scrollToRow}
  137. width={width}
  138. />
  139. )}
  140. </AutoSizer>
  141. ) : (
  142. <Placeholder height="100%" />
  143. )}
  144. {items?.length ? (
  145. <JumpButtons
  146. jump={showJumpUpButton ? 'up' : showJumpDownButton ? 'down' : undefined}
  147. onClick={onClickToJump}
  148. tableHeaderHeight={0}
  149. />
  150. ) : null}
  151. </TabItemContainer>
  152. </FluidHeight>
  153. );
  154. }
  155. export default Breadcrumbs;