diff --git a/packages/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView.tsx b/packages/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView.tsx index 698557ede3a..d808eb07f5f 100644 --- a/packages/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView.tsx +++ b/packages/jaeger-ui-components/src/TraceTimelineViewer/VirtualizedTraceView.tsx @@ -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; }; type VirtualizedTraceViewProps = TVirtualizedTraceViewOwnProps & TExtractUiFindFromStateReturn & TTraceTimeline; @@ -168,6 +182,7 @@ const memoizedGetClipping = memoizeOne(getClipping, isEqual); // export from tests export class UnthemedVirtualizedTraceView extends React.Component { listView: ListView | TNil; + topTraceViewRef = createRef(); constructor(props: VirtualizedTraceViewProps) { super(props); @@ -495,11 +510,16 @@ export class UnthemedVirtualizedTraceView extends React.Component { + const { topOfExploreViewRef } = this.props; + topOfExploreViewRef?.current?.scrollIntoView({ behavior: 'smooth' }); + }; + render() { const styles = getStyles(); const { scrollElement } = this.props; return ( -
+ <> -
+ + + ); } } diff --git a/packages/jaeger-ui-components/src/TraceTimelineViewer/index.tsx b/packages/jaeger-ui-components/src/TraceTimelineViewer/index.tsx index b00441e626e..e3e2c0e23bd 100644 --- a/packages/jaeger-ui-components/src/TraceTimelineViewer/index.tsx +++ b/packages/jaeger-ui-components/src/TraceTimelineViewer/index.tsx @@ -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; }; type State = { @@ -161,6 +162,7 @@ export class UnthemedTraceTimelineViewer extends React.PureComponent diff --git a/public/app/features/explore/Explore.tsx b/public/app/features/explore/Explore.tsx index b2141af9927..8e595a4b67c 100644 --- a/public/app/features/explore/Explore.tsx +++ b/public/app/features/explore/Explore.tsx @@ -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; export class Explore extends React.PureComponent { scrollElement: HTMLDivElement | undefined; absoluteTimeUnsubsciber: Unsubscribable | undefined; + topOfExploreViewRef = createRef(); constructor(props: Props) { super(props); @@ -305,6 +306,7 @@ export class Explore extends React.PureComponent { dataFrames={dataFrames} splitOpenFn={splitOpen} scrollElement={this.scrollElement} + topOfExploreViewRef={this.topOfExploreViewRef} /> ) ); @@ -337,7 +339,11 @@ export class Explore extends React.PureComponent { autoHeightMin={'100%'} scrollRefCallback={(scrollElement) => (this.scrollElement = scrollElement || undefined)} > - + {datasourceMissing ? this.renderEmptyState() : null} {datasourceInstance && (
diff --git a/public/app/features/explore/ExploreToolbar.tsx b/public/app/features/explore/ExploreToolbar.tsx index 3eea018fd92..1194f412046 100644 --- a/public/app/features/explore/ExploreToolbar.tsx +++ b/public/app/features/explore/ExploreToolbar.tsx @@ -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; } type Props = OwnProps & ConnectedProps; @@ -71,94 +72,97 @@ class UnConnectedExploreToolbar extends PureComponent { containerWidth, onChangeTimeZone, onChangeFiscalYearStartMonth, + topOfExploreViewRef, } = this.props; const showSmallDataSourcePicker = (splitted ? containerWidth < 700 : containerWidth < 800) || false; const showSmallTimePicker = splitted || containerWidth < 1210; return ( - createAndCopyShortLink(window.location.href)} - aria-label="Copy shortened link" +
+ createAndCopyShortLink(window.location.href)} + aria-label="Copy shortened link" + /> + ), + !datasourceMissing && ( + + ), + ].filter(Boolean)} + > + + {!splitted ? ( + split()} icon="columns" disabled={isLive}> + Split + + ) : ( + closeSplit(exploreId)} icon="times"> + Close + + )} + + {!isLive && ( + + )} + + + + - ), - !datasourceMissing && ( - - ), - ].filter(Boolean)} - > - - {!splitted ? ( - split()} icon="columns" disabled={isLive}> - Split - - ) : ( - closeSplit(exploreId)} icon="times"> - Close - - )} - {!isLive && ( - - )} + {refreshInterval && } - - - - - {refreshInterval && } - - {hasLiveOption && ( - - {(controls) => ( - - )} - - )} - - + {hasLiveOption && ( + + {(controls) => ( + + )} + + )} + + +
); } } diff --git a/public/app/features/explore/TraceView/TraceView.tsx b/public/app/features/explore/TraceView/TraceView.tsx index 106f8cedd3f..24a31dd3fdc 100644 --- a/public/app/features/explore/TraceView/TraceView.tsx +++ b/public/app/features/explore/TraceView/TraceView.tsx @@ -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; }; export function TraceView(props: Props) { @@ -176,6 +177,7 @@ export function TraceView(props: Props) { scrollElement={props.scrollElement} focusedSpanId={focusedSpanId} createFocusSpanLink={createFocusSpanLink} + topOfExploreViewRef={props.topOfExploreViewRef} /> ); diff --git a/public/app/features/explore/TraceView/TraceViewContainer.tsx b/public/app/features/explore/TraceView/TraceViewContainer.tsx index 740d8d4374a..1f0123aa708 100644 --- a/public/app/features/explore/TraceView/TraceViewContainer.tsx +++ b/public/app/features/explore/TraceView/TraceViewContainer.tsx @@ -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; } export function TraceViewContainer(props: Props) { - const { dataFrames, splitOpenFn, exploreId, scrollElement } = props; + const { dataFrames, splitOpenFn, exploreId, scrollElement, topOfExploreViewRef } = props; return ( @@ -20,6 +21,7 @@ export function TraceViewContainer(props: Props) { dataFrames={dataFrames} splitOpenFn={splitOpenFn} scrollElement={scrollElement} + topOfExploreViewRef={topOfExploreViewRef} /> ); diff --git a/public/app/features/explore/__snapshots__/Explore.test.tsx.snap b/public/app/features/explore/__snapshots__/Explore.test.tsx.snap index a27d07eef1e..59b523589fd 100644 --- a/public/app/features/explore/__snapshots__/Explore.test.tsx.snap +++ b/public/app/features/explore/__snapshots__/Explore.test.tsx.snap @@ -8,6 +8,11 @@ exports[`Explore should render component 1`] = `