mirror of
				https://github.com/grafana/grafana.git
				synced 2025-02-25 18:55:37 -06:00 
			
		
		
		
	Trace view scroll to top button (#46077)
* Trace view scroll to top button * Updated button css selector * Updates for scrolToTop * Updated snapshot * Updated render * Add smooth scrolling
This commit is contained in:
		@@ -17,7 +17,7 @@ import { css } from '@emotion/css';
 | 
			
		||||
 | 
			
		||||
import { isEqual } from 'lodash';
 | 
			
		||||
import memoizeOne from 'memoize-one';
 | 
			
		||||
import { stylesFactory, withTheme2 } from '@grafana/ui';
 | 
			
		||||
import { stylesFactory, withTheme2, ToolbarButton } from '@grafana/ui';
 | 
			
		||||
import { GrafanaTheme2, LinkModel } from '@grafana/data';
 | 
			
		||||
 | 
			
		||||
import ListView from './ListView';
 | 
			
		||||
@@ -38,6 +38,7 @@ import { SpanLinkFunc, TNil } from '../types';
 | 
			
		||||
import { TraceLog, TraceSpan, Trace, TraceKeyValuePair, TraceLink, TraceSpanReference } from '../types/trace';
 | 
			
		||||
import TTraceTimeline from '../types/TTraceTimeline';
 | 
			
		||||
import { PEER_SERVICE } from '../constants/tag-keys';
 | 
			
		||||
import { createRef, RefObject } from 'react';
 | 
			
		||||
 | 
			
		||||
type TExtractUiFindFromStateReturn = {
 | 
			
		||||
  uiFind: string | undefined;
 | 
			
		||||
@@ -51,6 +52,18 @@ const getStyles = stylesFactory(() => {
 | 
			
		||||
    row: css`
 | 
			
		||||
      width: 100%;
 | 
			
		||||
    `,
 | 
			
		||||
    scrollToTopButton: css`
 | 
			
		||||
      display: flex;
 | 
			
		||||
      flex-direction: column;
 | 
			
		||||
      justify-content: center;
 | 
			
		||||
      align-items: center;
 | 
			
		||||
      width: 40px;
 | 
			
		||||
      height: 40px;
 | 
			
		||||
      position: fixed;
 | 
			
		||||
      bottom: 30px;
 | 
			
		||||
      right: 30px;
 | 
			
		||||
      z-index: 1;
 | 
			
		||||
    `,
 | 
			
		||||
  };
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -89,6 +102,7 @@ type TVirtualizedTraceViewOwnProps = {
 | 
			
		||||
  scrollElement?: Element;
 | 
			
		||||
  focusedSpanId?: string;
 | 
			
		||||
  createFocusSpanLink: (traceId: string, spanId: string) => LinkModel;
 | 
			
		||||
  topOfExploreViewRef?: RefObject<HTMLDivElement>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type VirtualizedTraceViewProps = TVirtualizedTraceViewOwnProps & TExtractUiFindFromStateReturn & TTraceTimeline;
 | 
			
		||||
@@ -168,6 +182,7 @@ const memoizedGetClipping = memoizeOne(getClipping, isEqual);
 | 
			
		||||
// export from tests
 | 
			
		||||
export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTraceViewProps> {
 | 
			
		||||
  listView: ListView | TNil;
 | 
			
		||||
  topTraceViewRef = createRef<HTMLDivElement>();
 | 
			
		||||
 | 
			
		||||
  constructor(props: VirtualizedTraceViewProps) {
 | 
			
		||||
    super(props);
 | 
			
		||||
@@ -495,11 +510,16 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  scrollToTop = () => {
 | 
			
		||||
    const { topOfExploreViewRef } = this.props;
 | 
			
		||||
    topOfExploreViewRef?.current?.scrollIntoView({ behavior: 'smooth' });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const styles = getStyles();
 | 
			
		||||
    const { scrollElement } = this.props;
 | 
			
		||||
    return (
 | 
			
		||||
      <div>
 | 
			
		||||
      <>
 | 
			
		||||
        <ListView
 | 
			
		||||
          ref={this.setListView}
 | 
			
		||||
          dataLength={this.getRowStates().length}
 | 
			
		||||
@@ -513,7 +533,14 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
 | 
			
		||||
          windowScroller={false}
 | 
			
		||||
          scrollElement={scrollElement}
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
        <ToolbarButton
 | 
			
		||||
          className={styles.scrollToTopButton}
 | 
			
		||||
          onClick={this.scrollToTop}
 | 
			
		||||
          title="Scroll to top"
 | 
			
		||||
          icon="arrow-up"
 | 
			
		||||
        ></ToolbarButton>
 | 
			
		||||
      </>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import React, { RefObject } from 'react';
 | 
			
		||||
import { css } from '@emotion/css';
 | 
			
		||||
import { GrafanaTheme2, LinkModel } from '@grafana/data';
 | 
			
		||||
import { stylesFactory, withTheme2 } from '@grafana/ui';
 | 
			
		||||
@@ -106,6 +106,7 @@ type TProps = TExtractUiFindFromStateReturn & {
 | 
			
		||||
  scrollElement?: Element;
 | 
			
		||||
  focusedSpanId?: string;
 | 
			
		||||
  createFocusSpanLink: (traceId: string, spanId: string) => LinkModel;
 | 
			
		||||
  topOfExploreViewRef?: RefObject<HTMLDivElement>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type State = {
 | 
			
		||||
@@ -161,6 +162,7 @@ export class UnthemedTraceTimelineViewer extends React.PureComponent<TProps, Sta
 | 
			
		||||
      createLinkToExternalSpan,
 | 
			
		||||
      traceTimeline,
 | 
			
		||||
      theme,
 | 
			
		||||
      topOfExploreViewRef,
 | 
			
		||||
      ...rest
 | 
			
		||||
    } = this.props;
 | 
			
		||||
    const { trace } = rest;
 | 
			
		||||
@@ -191,6 +193,7 @@ export class UnthemedTraceTimelineViewer extends React.PureComponent<TProps, Sta
 | 
			
		||||
            {...traceTimeline}
 | 
			
		||||
            setSpanNameColumnWidth={setSpanNameColumnWidth}
 | 
			
		||||
            currentViewRangeTime={viewRange.time.current}
 | 
			
		||||
            topOfExploreViewRef={topOfExploreViewRef}
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
      </ExternalLinkContext.Provider>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import React, { createRef } from 'react';
 | 
			
		||||
import { css, cx } from '@emotion/css';
 | 
			
		||||
import { compose } from 'redux';
 | 
			
		||||
import { connect, ConnectedProps } from 'react-redux';
 | 
			
		||||
@@ -101,6 +101,7 @@ export type Props = ExploreProps & ConnectedProps<typeof connector>;
 | 
			
		||||
export class Explore extends React.PureComponent<Props, ExploreState> {
 | 
			
		||||
  scrollElement: HTMLDivElement | undefined;
 | 
			
		||||
  absoluteTimeUnsubsciber: Unsubscribable | undefined;
 | 
			
		||||
  topOfExploreViewRef = createRef<HTMLDivElement>();
 | 
			
		||||
 | 
			
		||||
  constructor(props: Props) {
 | 
			
		||||
    super(props);
 | 
			
		||||
@@ -305,6 +306,7 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
 | 
			
		||||
          dataFrames={dataFrames}
 | 
			
		||||
          splitOpenFn={splitOpen}
 | 
			
		||||
          scrollElement={this.scrollElement}
 | 
			
		||||
          topOfExploreViewRef={this.topOfExploreViewRef}
 | 
			
		||||
        />
 | 
			
		||||
      )
 | 
			
		||||
    );
 | 
			
		||||
@@ -337,7 +339,11 @@ export class Explore extends React.PureComponent<Props, ExploreState> {
 | 
			
		||||
        autoHeightMin={'100%'}
 | 
			
		||||
        scrollRefCallback={(scrollElement) => (this.scrollElement = scrollElement || undefined)}
 | 
			
		||||
      >
 | 
			
		||||
        <ExploreToolbar exploreId={exploreId} onChangeTime={this.onChangeTime} />
 | 
			
		||||
        <ExploreToolbar
 | 
			
		||||
          exploreId={exploreId}
 | 
			
		||||
          onChangeTime={this.onChangeTime}
 | 
			
		||||
          topOfExploreViewRef={this.topOfExploreViewRef}
 | 
			
		||||
        />
 | 
			
		||||
        {datasourceMissing ? this.renderEmptyState() : null}
 | 
			
		||||
        {datasourceInstance && (
 | 
			
		||||
          <div className="explore-container">
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import React, { PureComponent } from 'react';
 | 
			
		||||
import React, { PureComponent, RefObject } from 'react';
 | 
			
		||||
import { connect, ConnectedProps } from 'react-redux';
 | 
			
		||||
import { ExploreId } from 'app/types/explore';
 | 
			
		||||
import { PageToolbar, SetInterval, ToolbarButton, ToolbarButtonRow } from '@grafana/ui';
 | 
			
		||||
@@ -23,6 +23,7 @@ import { AddToDashboard } from './AddToDashboard';
 | 
			
		||||
interface OwnProps {
 | 
			
		||||
  exploreId: ExploreId;
 | 
			
		||||
  onChangeTime: (range: RawTimeRange, changedByScanner?: boolean) => void;
 | 
			
		||||
  topOfExploreViewRef?: RefObject<HTMLDivElement>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Props = OwnProps & ConnectedProps<typeof connector>;
 | 
			
		||||
@@ -71,12 +72,14 @@ class UnConnectedExploreToolbar extends PureComponent<Props> {
 | 
			
		||||
      containerWidth,
 | 
			
		||||
      onChangeTimeZone,
 | 
			
		||||
      onChangeFiscalYearStartMonth,
 | 
			
		||||
      topOfExploreViewRef,
 | 
			
		||||
    } = this.props;
 | 
			
		||||
 | 
			
		||||
    const showSmallDataSourcePicker = (splitted ? containerWidth < 700 : containerWidth < 800) || false;
 | 
			
		||||
    const showSmallTimePicker = splitted || containerWidth < 1210;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <div ref={topOfExploreViewRef}>
 | 
			
		||||
        <PageToolbar
 | 
			
		||||
          title={exploreId === ExploreId.left ? 'Explore' : undefined}
 | 
			
		||||
          pageIcon={exploreId === ExploreId.left ? 'compass' : undefined}
 | 
			
		||||
@@ -159,6 +162,7 @@ class UnConnectedExploreToolbar extends PureComponent<Props> {
 | 
			
		||||
            )}
 | 
			
		||||
          </ToolbarButtonRow>
 | 
			
		||||
        </PageToolbar>
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
 | 
			
		||||
import { getTimeZone } from 'app/features/profile/state/selectors';
 | 
			
		||||
import { StoreState } from 'app/types';
 | 
			
		||||
import { ExploreId } from 'app/types/explore';
 | 
			
		||||
import React, { useCallback, useMemo, useState } from 'react';
 | 
			
		||||
import React, { RefObject, useCallback, useMemo, useState } from 'react';
 | 
			
		||||
import { useDispatch, useSelector } from 'react-redux';
 | 
			
		||||
import { changePanelState } from '../state/explorePane';
 | 
			
		||||
import { createSpanLinkFactory } from './createSpanLink';
 | 
			
		||||
@@ -43,6 +43,7 @@ type Props = {
 | 
			
		||||
  splitOpenFn: SplitOpen;
 | 
			
		||||
  exploreId: ExploreId;
 | 
			
		||||
  scrollElement?: Element;
 | 
			
		||||
  topOfExploreViewRef?: RefObject<HTMLDivElement>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function TraceView(props: Props) {
 | 
			
		||||
@@ -176,6 +177,7 @@ export function TraceView(props: Props) {
 | 
			
		||||
        scrollElement={props.scrollElement}
 | 
			
		||||
        focusedSpanId={focusedSpanId}
 | 
			
		||||
        createFocusSpanLink={createFocusSpanLink}
 | 
			
		||||
        topOfExploreViewRef={props.topOfExploreViewRef}
 | 
			
		||||
      />
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import React, { RefObject } from 'react';
 | 
			
		||||
import { Collapse } from '@grafana/ui';
 | 
			
		||||
import { DataFrame, SplitOpen } from '@grafana/data';
 | 
			
		||||
import { TraceView } from './TraceView';
 | 
			
		||||
@@ -9,9 +9,10 @@ interface Props {
 | 
			
		||||
  splitOpenFn: SplitOpen;
 | 
			
		||||
  exploreId: ExploreId;
 | 
			
		||||
  scrollElement?: Element;
 | 
			
		||||
  topOfExploreViewRef?: RefObject<HTMLDivElement>;
 | 
			
		||||
}
 | 
			
		||||
export function TraceViewContainer(props: Props) {
 | 
			
		||||
  const { dataFrames, splitOpenFn, exploreId, scrollElement } = props;
 | 
			
		||||
  const { dataFrames, splitOpenFn, exploreId, scrollElement, topOfExploreViewRef } = props;
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Collapse label="Trace View" isOpen>
 | 
			
		||||
@@ -20,6 +21,7 @@ export function TraceViewContainer(props: Props) {
 | 
			
		||||
        dataFrames={dataFrames}
 | 
			
		||||
        splitOpenFn={splitOpenFn}
 | 
			
		||||
        scrollElement={scrollElement}
 | 
			
		||||
        topOfExploreViewRef={topOfExploreViewRef}
 | 
			
		||||
      />
 | 
			
		||||
    </Collapse>
 | 
			
		||||
  );
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,11 @@ exports[`Explore should render component 1`] = `
 | 
			
		||||
  <Connect(UnConnectedExploreToolbar)
 | 
			
		||||
    exploreId="left"
 | 
			
		||||
    onChangeTime={[Function]}
 | 
			
		||||
    topOfExploreViewRef={
 | 
			
		||||
      Object {
 | 
			
		||||
        "current": null,
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  />
 | 
			
		||||
  <div
 | 
			
		||||
    className="explore-container"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user