index.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import {memo, useMemo, useRef} from 'react';
  2. import {
  3. AutoSizer,
  4. CellMeasurer,
  5. List as ReactVirtualizedList,
  6. ListRowProps,
  7. } from 'react-virtualized';
  8. import {useQuery} from '@tanstack/react-query';
  9. import Placeholder from 'sentry/components/placeholder';
  10. import JumpButtons from 'sentry/components/replays/jumpButtons';
  11. import {useReplayContext} from 'sentry/components/replays/replayContext';
  12. import {t} from 'sentry/locale';
  13. import useCrumbHandlers from 'sentry/utils/replays/hooks/useCrumbHandlers';
  14. import type ReplayReader from 'sentry/utils/replays/replayReader';
  15. import DomFilters from 'sentry/views/replays/detail/domMutations/domFilters';
  16. import DomMutationRow from 'sentry/views/replays/detail/domMutations/domMutationRow';
  17. import useDomFilters from 'sentry/views/replays/detail/domMutations/useDomFilters';
  18. import FilterLoadingIndicator from 'sentry/views/replays/detail/filterLoadingIndicator';
  19. import FluidHeight from 'sentry/views/replays/detail/layout/fluidHeight';
  20. import NoRowRenderer from 'sentry/views/replays/detail/noRowRenderer';
  21. import TabItemContainer from 'sentry/views/replays/detail/tabItemContainer';
  22. import useVirtualizedList from 'sentry/views/replays/detail/useVirtualizedList';
  23. // Ensure this object is created once as it is an input to
  24. // `useVirtualizedList`'s memoization
  25. const cellMeasurer = {
  26. fixedWidth: true,
  27. minHeight: 82,
  28. };
  29. function useExtractedDomNodes({replay}: {replay: null | ReplayReader}) {
  30. return useQuery(['getDomNodes', replay], () => replay?.getDomNodes() ?? [], {
  31. enabled: Boolean(replay),
  32. initialData: [],
  33. cacheTime: Infinity,
  34. });
  35. }
  36. function DomMutations() {
  37. const {currentTime, currentHoverTime, replay} = useReplayContext();
  38. const {data: actions, isFetching} = useExtractedDomNodes({replay});
  39. const {onMouseEnter, onMouseLeave, onClickTimestamp} = useCrumbHandlers();
  40. const startTimestampMs = replay?.getReplay()?.started_at?.getTime() ?? 0;
  41. const filterProps = useDomFilters({actions: actions || []});
  42. const {items, setSearchTerm} = filterProps;
  43. const clearSearchTerm = () => setSearchTerm('');
  44. const listRef = useRef<ReactVirtualizedList>(null);
  45. const deps = useMemo(() => [items], [items]);
  46. const {cache, updateList} = useVirtualizedList({
  47. cellMeasurer,
  48. ref: listRef,
  49. deps,
  50. });
  51. const renderRow = ({index, key, style, parent}: ListRowProps) => {
  52. const mutation = items[index];
  53. return (
  54. <CellMeasurer
  55. cache={cache}
  56. columnIndex={0}
  57. key={key}
  58. parent={parent}
  59. rowIndex={index}
  60. >
  61. <DomMutationRow
  62. currentHoverTime={currentHoverTime}
  63. currentTime={currentTime}
  64. onMouseEnter={onMouseEnter}
  65. onMouseLeave={onMouseLeave}
  66. mutation={mutation}
  67. onClickTimestamp={onClickTimestamp}
  68. startTimestampMs={startTimestampMs}
  69. style={style}
  70. />
  71. </CellMeasurer>
  72. );
  73. };
  74. const showJumpUpButton = false;
  75. const showJumpDownButton = false;
  76. return (
  77. <FluidHeight>
  78. <FilterLoadingIndicator isLoading={isFetching}>
  79. <DomFilters actions={actions} {...filterProps} />
  80. </FilterLoadingIndicator>
  81. <TabItemContainer data-test-id="replay-details-dom-events-tab">
  82. {isFetching || !actions ? (
  83. <Placeholder height="100%" />
  84. ) : (
  85. <AutoSizer onResize={updateList}>
  86. {({width, height}) => (
  87. <ReactVirtualizedList
  88. deferredMeasurementCache={cache}
  89. height={height}
  90. noRowsRenderer={() => (
  91. <NoRowRenderer
  92. unfilteredItems={actions}
  93. clearSearchTerm={clearSearchTerm}
  94. >
  95. {t('No DOM events recorded')}
  96. </NoRowRenderer>
  97. )}
  98. overscanRowCount={5}
  99. ref={listRef}
  100. rowCount={items.length}
  101. rowHeight={cache.rowHeight}
  102. rowRenderer={renderRow}
  103. width={width}
  104. />
  105. )}
  106. </AutoSizer>
  107. )}
  108. <JumpButtons
  109. jump={showJumpUpButton ? 'up' : showJumpDownButton ? 'down' : undefined}
  110. onClick={() => {}}
  111. tableHeaderHeight={0}
  112. />
  113. </TabItemContainer>
  114. </FluidHeight>
  115. );
  116. }
  117. export default memo(DomMutations);