Browse Source

chore(performance-trace-details): Converted transaction bar component to functional. (#60704)

This will help use react-query hooks for the trace view span embedding
work.

Co-authored-by: Abdullah Khan <abdullahkhan@PG9Y57YDXQ.local>
Abdkhan14 1 year ago
parent
commit
20413343ac
1 changed files with 130 additions and 144 deletions
  1. 130 144
      static/app/views/performance/traceDetails/transactionBar.tsx

+ 130 - 144
static/app/views/performance/traceDetails/transactionBar.tsx

@@ -1,4 +1,4 @@
-import {Component, createRef, Fragment} from 'react';
+import {createRef, Fragment, useCallback, useEffect, useState} from 'react';
 import {browserHistory} from 'react-router';
 import styled from '@emotion/styled';
 import {Location} from 'history';
@@ -92,83 +92,89 @@ type Props = {
   onlyOrphanErrors?: boolean;
 };
 
-type State = {
-  showDetail: boolean;
-};
+function TransactionBar(props: Props) {
+  const [showDetail, setShowDetail] = useState(false);
+  const transactionRowDOMRef = createRef<HTMLDivElement>();
+  const transactionTitleRef = createRef<HTMLDivElement>();
+  let spanContentRef: HTMLDivElement | null = null;
+
+  const handleWheel = useCallback(
+    (event: WheelEvent) => {
+      // https://stackoverflow.com/q/57358640
+      // https://github.com/facebook/react/issues/14856
+      if (Math.abs(event.deltaY) > Math.abs(event.deltaX)) {
+        return;
+      }
 
-class TransactionBar extends Component<Props, State> {
-  state: State = {
-    showDetail: false,
-  };
+      event.preventDefault();
+      event.stopPropagation();
+
+      if (Math.abs(event.deltaY) === Math.abs(event.deltaX)) {
+        return;
+      }
+
+      const {onWheel} = props;
+      onWheel(event.deltaX);
+    },
+    [props]
+  );
+
+  const scrollIntoView = useCallback(() => {
+    const element = transactionRowDOMRef.current;
+    if (!element) {
+      return;
+    }
+    const boundingRect = element.getBoundingClientRect();
+    const offset = boundingRect.top + window.scrollY;
+    setShowDetail(true);
+    window.scrollTo(0, offset);
+  }, [transactionRowDOMRef]);
 
-  componentDidMount() {
-    const {location, transaction} = this.props;
+  useEffect(() => {
+    const {location, transaction} = props;
+    const transactionTitleRefCurrentCopy = transactionTitleRef.current;
 
     if (
       'event_id' in transaction &&
       transactionTargetHash(transaction.event_id) === location.hash
     ) {
-      this.scrollIntoView();
+      scrollIntoView();
     }
 
-    if (this.transactionTitleRef.current) {
-      this.transactionTitleRef.current.addEventListener('wheel', this.handleWheel, {
+    if (transactionTitleRefCurrentCopy) {
+      transactionTitleRefCurrentCopy.addEventListener('wheel', handleWheel, {
         passive: false,
       });
     }
-  }
-
-  componentWillUnmount() {
-    if (this.transactionTitleRef.current) {
-      this.transactionTitleRef.current.removeEventListener('wheel', this.handleWheel);
-    }
-  }
 
-  transactionRowDOMRef = createRef<HTMLDivElement>();
-  transactionTitleRef = createRef<HTMLDivElement>();
-  spanContentRef: HTMLDivElement | null = null;
+    return () => {
+      if (transactionTitleRefCurrentCopy) {
+        transactionTitleRefCurrentCopy.removeEventListener('wheel', handleWheel);
+      }
+    };
+  }, [handleWheel, props, scrollIntoView, transactionTitleRef]);
 
-  handleRowCellClick = () => {
-    const {transaction, organization} = this.props;
+  const handleRowCellClick = () => {
+    const {transaction, organization} = props;
 
     if (isTraceError(transaction)) {
       browserHistory.push(generateIssueEventTarget(transaction, organization));
     }
 
     if (isTraceTransaction<TraceFullDetailed>(transaction)) {
-      this.setState(state => ({
-        showDetail: !state.showDetail,
-      }));
+      setShowDetail(prev => !prev);
     }
   };
 
-  getCurrentOffset() {
-    const {transaction} = this.props;
+  const getCurrentOffset = () => {
+    const {transaction} = props;
     const {generation} = transaction;
 
     return getOffset(generation);
-  }
-
-  handleWheel = (event: WheelEvent) => {
-    // https://stackoverflow.com/q/57358640
-    // https://github.com/facebook/react/issues/14856
-    if (Math.abs(event.deltaY) > Math.abs(event.deltaX)) {
-      return;
-    }
-
-    event.preventDefault();
-    event.stopPropagation();
-
-    if (Math.abs(event.deltaY) === Math.abs(event.deltaX)) {
-      return;
-    }
-
-    const {onWheel} = this.props;
-    onWheel(event.deltaX);
   };
 
-  renderMeasurements() {
-    const {measurements, generateBounds} = this.props;
+  const renderMeasurements = () => {
+    const {measurements, generateBounds} = props;
     if (!measurements) {
       return null;
     }
@@ -198,10 +204,10 @@ class TransactionBar extends Component<Props, State> {
         })}
       </Fragment>
     );
-  }
+  };
 
-  renderConnector(hasToggle: boolean) {
-    const {continuingDepths, isExpanded, isOrphan, isLast, transaction} = this.props;
+  const renderConnector = (hasToggle: boolean) => {
+    const {continuingDepths, isExpanded, isOrphan, isLast, transaction} = props;
 
     const {generation = 0} = transaction;
     const eventId =
@@ -262,11 +268,11 @@ class TransactionBar extends Component<Props, State> {
         {connectorBars}
       </TreeConnector>
     );
-  }
+  };
 
-  renderToggle(errored: boolean) {
-    const {isExpanded, transaction, toggleExpandedState, numOfOrphanErrors} = this.props;
-    const left = this.getCurrentOffset();
+  const renderToggle = (errored: boolean) => {
+    const {isExpanded, transaction, toggleExpandedState, numOfOrphanErrors} = props;
+    const left = getCurrentOffset();
 
     const hasOrphanErrors = numOfOrphanErrors && numOfOrphanErrors > 0;
     const childrenLength =
@@ -275,7 +281,7 @@ class TransactionBar extends Component<Props, State> {
     if (childrenLength <= 0 && !hasOrphanErrors) {
       return (
         <TreeToggleContainer style={{left: `${left}px`}}>
-          {this.renderConnector(false)}
+          {renderConnector(false)}
         </TreeToggleContainer>
       );
     }
@@ -284,7 +290,7 @@ class TransactionBar extends Component<Props, State> {
 
     return (
       <TreeToggleContainer style={{left: `${left}px`}} hasToggler>
-        {this.renderConnector(true)}
+        {renderConnector(true)}
         <TreeToggle
           disabled={isRoot}
           isExpanded={isExpanded}
@@ -308,13 +314,12 @@ class TransactionBar extends Component<Props, State> {
         </TreeToggle>
       </TreeToggleContainer>
     );
-  }
+  };
 
-  // TODO: Use ScrollbarManager to bring autoscrolling here
-  renderTitle(_: ScrollbarManager.ScrollbarManagerChildrenProps) {
+  const renderTitle = (_: ScrollbarManager.ScrollbarManagerChildrenProps) => {
     const {organization, transaction, addContentSpanBarRef, removeContentSpanBarRef} =
-      this.props;
-    const left = this.getCurrentOffset();
+      props;
+    const left = getCurrentOffset();
     const errored = isTraceTransaction<TraceFullDetailed>(transaction)
       ? transaction.errors &&
         transaction.errors.length + transaction.performance_issues.length > 0
@@ -372,15 +377,15 @@ class TransactionBar extends Component<Props, State> {
       <RowTitleContainer
         ref={ref => {
           if (!ref) {
-            removeContentSpanBarRef(this.spanContentRef);
+            removeContentSpanBarRef(spanContentRef);
             return;
           }
 
           addContentSpanBarRef(ref);
-          this.spanContentRef = ref;
+          spanContentRef = ref;
         }}
       >
-        {this.renderToggle(errored)}
+        {renderToggle(errored)}
         <RowTitle
           style={{
             left: `${left}px`,
@@ -391,12 +396,12 @@ class TransactionBar extends Component<Props, State> {
         </RowTitle>
       </RowTitleContainer>
     );
-  }
+  };
 
-  renderDivider(
+  const renderDivider = (
     dividerHandlerChildrenProps: DividerHandlerManager.DividerHandlerManagerChildrenProps
-  ) {
-    if (this.state.showDetail) {
+  ) => {
+    if (showDetail) {
       // Mock component to preserve layout spacing
       return (
         <DividerLine
@@ -433,11 +438,11 @@ class TransactionBar extends Component<Props, State> {
         }}
       />
     );
-  }
+  };
 
-  renderGhostDivider(
+  const renderGhostDivider = (
     dividerHandlerChildrenProps: DividerHandlerManager.DividerHandlerManagerChildrenProps
-  ) {
+  ) => {
     const {dividerPosition, addGhostDividerLineRef} = dividerHandlerChildrenProps;
 
     return (
@@ -462,10 +467,10 @@ class TransactionBar extends Component<Props, State> {
         />
       </DividerLineGhostContainer>
     );
-  }
+  };
 
-  renderErrorBadge() {
-    const {transaction} = this.props;
+  const renderErrorBadge = () => {
+    const {transaction} = props;
 
     if (
       isTraceRoot(transaction) ||
@@ -476,11 +481,10 @@ class TransactionBar extends Component<Props, State> {
     }
 
     return <ErrorBadge />;
-  }
+  };
 
-  renderRectangle() {
-    const {transaction, traceInfo, barColor} = this.props;
-    const {showDetail} = this.state;
+  const renderRectangle = () => {
+    const {transaction, traceInfo, barColor} = props;
 
     // Use 1 as the difference in the case that startTimestamp === endTimestamp
     const delta = Math.abs(traceInfo.endTimestamp - traceInfo.startTimestamp) || 1;
@@ -505,12 +509,12 @@ class TransactionBar extends Component<Props, State> {
           width: toPercent(widthPercentage || 0),
         }}
       >
-        {this.renderPerformanceIssues()}
+        {renderPerformanceIssues()}
         {isTraceError(transaction) ? (
           <ErrorBadge />
         ) : (
           <Fragment>
-            {this.renderErrorBadge()}
+            {renderErrorBadge()}
             <DurationPill
               durationDisplay={getDurationDisplay({
                 left: startPercentage,
@@ -524,10 +528,10 @@ class TransactionBar extends Component<Props, State> {
         )}
       </StyledRowRectangle>
     );
-  }
+  };
 
-  renderPerformanceIssues() {
-    const {transaction, barColor} = this.props;
+  const renderPerformanceIssues = () => {
+    const {transaction, barColor} = props;
     if (isTraceError(transaction) || isTraceRoot(transaction)) {
       return null;
     }
@@ -553,17 +557,16 @@ class TransactionBar extends Component<Props, State> {
       );
     }
     return rows;
-  }
+  };
 
-  renderHeader({
+  const renderHeader = ({
     dividerHandlerChildrenProps,
     scrollbarManagerChildrenProps,
   }: {
     dividerHandlerChildrenProps: DividerHandlerManager.DividerHandlerManagerChildrenProps;
     scrollbarManagerChildrenProps: ScrollbarManager.ScrollbarManagerChildrenProps;
-  }) {
-    const {hasGuideAnchor, index, transaction, onlyOrphanErrors = false} = this.props;
-    const {showDetail} = this.state;
+  }) => {
+    const {hasGuideAnchor, index, transaction, onlyOrphanErrors = false} = props;
     const {dividerPosition} = dividerHandlerChildrenProps;
     const hideDurationRectangle = isTraceRoot(transaction) && onlyOrphanErrors;
 
@@ -577,16 +580,14 @@ class TransactionBar extends Component<Props, State> {
             paddingTop: 0,
           }}
           showDetail={showDetail}
-          onClick={this.handleRowCellClick}
-          ref={this.transactionTitleRef}
+          onClick={handleRowCellClick}
+          ref={transactionTitleRef}
         >
           <GuideAnchor target="trace_view_guide_row" disabled={!hasGuideAnchor}>
-            {this.renderTitle(scrollbarManagerChildrenProps)}
+            {renderTitle(scrollbarManagerChildrenProps)}
           </GuideAnchor>
         </RowCell>
-        <DividerContainer>
-          {this.renderDivider(dividerHandlerChildrenProps)}
-        </DividerContainer>
+        <DividerContainer>{renderDivider(dividerHandlerChildrenProps)}</DividerContainer>
         <RowCell
           data-test-id="transaction-row-duration"
           data-type="span-row-cell"
@@ -597,32 +598,21 @@ class TransactionBar extends Component<Props, State> {
             overflow: 'visible',
           }}
           showDetail={showDetail}
-          onClick={this.handleRowCellClick}
+          onClick={handleRowCellClick}
         >
           <RowReplayTimeIndicators />
           <GuideAnchor target="trace_view_guide_row_details" disabled={!hasGuideAnchor}>
-            {!hideDurationRectangle && this.renderRectangle()}
-            {this.renderMeasurements()}
+            {!hideDurationRectangle && renderRectangle()}
+            {renderMeasurements()}
           </GuideAnchor>
         </RowCell>
-        {!showDetail && this.renderGhostDivider(dividerHandlerChildrenProps)}
+        {!showDetail && renderGhostDivider(dividerHandlerChildrenProps)}
       </RowCellContainer>
     );
-  }
-
-  scrollIntoView = () => {
-    const element = this.transactionRowDOMRef.current;
-    if (!element) {
-      return;
-    }
-    const boundingRect = element.getBoundingClientRect();
-    const offset = boundingRect.top + window.scrollY;
-    this.setState({showDetail: true}, () => window.scrollTo(0, offset));
   };
 
-  renderDetail() {
-    const {location, organization, isVisible, transaction} = this.props;
-    const {showDetail} = this.state;
+  const renderDetail = () => {
+    const {location, organization, isVisible, transaction} = props;
 
     if (isTraceError(transaction) || isTraceRoot(transaction)) {
       return null;
@@ -637,39 +627,35 @@ class TransactionBar extends Component<Props, State> {
         location={location}
         organization={organization}
         transaction={transaction}
-        scrollIntoView={this.scrollIntoView}
+        scrollIntoView={scrollIntoView}
       />
     );
-  }
+  };
 
-  render() {
-    const {isVisible, transaction} = this.props;
-    const {showDetail} = this.state;
-    return (
-      <StyledRow
-        ref={this.transactionRowDOMRef}
-        visible={isVisible}
-        showBorder={showDetail}
-        cursor={
-          isTraceTransaction<TraceFullDetailed>(transaction) ? 'pointer' : 'default'
-        }
-      >
-        <ScrollbarManager.Consumer>
-          {scrollbarManagerChildrenProps => (
-            <DividerHandlerManager.Consumer>
-              {dividerHandlerChildrenProps =>
-                this.renderHeader({
-                  dividerHandlerChildrenProps,
-                  scrollbarManagerChildrenProps,
-                })
-              }
-            </DividerHandlerManager.Consumer>
-          )}
-        </ScrollbarManager.Consumer>
-        {this.renderDetail()}
-      </StyledRow>
-    );
-  }
+  const {isVisible, transaction} = props;
+
+  return (
+    <StyledRow
+      ref={transactionRowDOMRef}
+      visible={isVisible}
+      showBorder={showDetail}
+      cursor={isTraceTransaction<TraceFullDetailed>(transaction) ? 'pointer' : 'default'}
+    >
+      <ScrollbarManager.Consumer>
+        {scrollbarManagerChildrenProps => (
+          <DividerHandlerManager.Consumer>
+            {dividerHandlerChildrenProps =>
+              renderHeader({
+                dividerHandlerChildrenProps,
+                scrollbarManagerChildrenProps,
+              })
+            }
+          </DividerHandlerManager.Consumer>
+        )}
+      </ScrollbarManager.Consumer>
+      {renderDetail()}
+    </StyledRow>
+  );
 }
 
 function getOffset(generation) {