diff --git a/public/app/features/explore/TraceView/components/TracePageHeader/SearchBar/NewTracePageSearchBar.test.tsx b/public/app/features/explore/TraceView/components/TracePageHeader/SearchBar/NewTracePageSearchBar.test.tsx index 2a252b606d8..f0ff1a733a9 100644 --- a/public/app/features/explore/TraceView/components/TracePageHeader/SearchBar/NewTracePageSearchBar.test.tsx +++ b/public/app/features/explore/TraceView/components/TracePageHeader/SearchBar/NewTracePageSearchBar.test.tsx @@ -16,12 +16,14 @@ import { render, screen } from '@testing-library/react'; import React from 'react'; import { defaultFilters } from '../../../useSearch'; +import { trace } from '../TracePageHeader.test'; import NewTracePageSearchBar from './NewTracePageSearchBar'; describe('', () => { const NewTracePageSearchBarWithProps = (props: { matches: string[] | undefined }) => { const searchBarProps = { + trace: trace, search: defaultFilters, spanFilterMatches: props.matches ? new Set(props.matches) : undefined, showSpanFilterMatchesOnly: false, diff --git a/public/app/features/explore/TraceView/components/TracePageHeader/SearchBar/NewTracePageSearchBar.tsx b/public/app/features/explore/TraceView/components/TracePageHeader/SearchBar/NewTracePageSearchBar.tsx index fa649bcd4d9..ad36c753ddd 100644 --- a/public/app/features/explore/TraceView/components/TracePageHeader/SearchBar/NewTracePageSearchBar.tsx +++ b/public/app/features/explore/TraceView/components/TracePageHeader/SearchBar/NewTracePageSearchBar.tsx @@ -20,11 +20,13 @@ import { Button, Switch, useStyles2 } from '@grafana/ui'; import { getButtonStyles } from '@grafana/ui/src/components/Button'; import { SearchProps } from '../../../useSearch'; +import { Trace } from '../../types'; import { convertTimeFilter } from '../../utils/filter-spans'; import NextPrevResult from './NextPrevResult'; export type TracePageSearchBarProps = { + trace: Trace; search: SearchProps; spanFilterMatches: Set | undefined; showSpanFilterMatchesOnly: boolean; @@ -33,13 +35,13 @@ export type TracePageSearchBarProps = { setFocusedSpanIndexForSearch: Dispatch>; setFocusedSpanIdForSearch: Dispatch>; datasourceType: string; - totalSpans: number; clear: () => void; showSpanFilters: boolean; }; export default memo(function NewTracePageSearchBar(props: TracePageSearchBarProps) { const { + trace, search, spanFilterMatches, showSpanFilterMatchesOnly, @@ -48,7 +50,6 @@ export default memo(function NewTracePageSearchBar(props: TracePageSearchBarProp setFocusedSpanIndexForSearch, setFocusedSpanIdForSearch, datasourceType, - totalSpans, clear, showSpanFilters, } = props; @@ -100,12 +101,12 @@ export default memo(function NewTracePageSearchBar(props: TracePageSearchBarProp
diff --git a/public/app/features/explore/TraceView/components/TracePageHeader/SearchBar/NextPrevResult.test.tsx b/public/app/features/explore/TraceView/components/TracePageHeader/SearchBar/NextPrevResult.test.tsx index 149dbcb0616..fa042b933ab 100644 --- a/public/app/features/explore/TraceView/components/TracePageHeader/SearchBar/NextPrevResult.test.tsx +++ b/public/app/features/explore/TraceView/components/TracePageHeader/SearchBar/NextPrevResult.test.tsx @@ -19,6 +19,7 @@ import React, { useState } from 'react'; import { createTheme } from '@grafana/data'; import { defaultFilters } from '../../../useSearch'; +import { trace } from '../TracePageHeader.test'; import NextPrevResult, { getStyles } from './NextPrevResult'; @@ -37,6 +38,7 @@ describe('', () => { const NextPrevResultWithProps = (props: { matches: string[] | undefined }) => { const [focusedSpanIndexForSearch, setFocusedSpanIndexForSearch] = useState(-1); const searchBarProps = { + trace: trace, search: defaultFilters, spanFilterMatches: props.matches ? new Set(props.matches) : undefined, showSpanFilterMatchesOnly: false, @@ -46,7 +48,6 @@ describe('', () => { setFocusedSpanIndexForSearch: setFocusedSpanIndexForSearch, datasourceType: '', clear: jest.fn(), - totalSpans: 100, showSpanFilters: true, }; @@ -70,11 +71,11 @@ describe('', () => { it('renders total spans', async () => { render(); - expect(screen.getByText('100 spans')).toBeDefined(); + expect(screen.getByText('3 spans')).toBeDefined(); }); it('renders buttons that can be used to search if filters added', () => { - render(); + render(); const nextResButton = screen.queryByRole('button', { name: 'Next result button' }); const prevResButton = screen.queryByRole('button', { name: 'Prev result button' }); expect(nextResButton).toBeInTheDocument(); @@ -85,7 +86,7 @@ describe('', () => { }); it('renders correctly when moving through matches', async () => { - render(); + render(); const nextResButton = screen.queryByRole('button', { name: 'Next result button' }); const prevResButton = screen.queryByRole('button', { name: 'Prev result button' }); expect(screen.getByText('3 matches')).toBeDefined(); @@ -106,7 +107,7 @@ describe('', () => { it('renders correctly when there are no matches i.e. too many filters added', async () => { const { container } = render(); const theme = createTheme(); - const tooltip = container.querySelector('.' + getStyles(theme, true).matchesTooltip); + const tooltip = container.querySelector('.' + getStyles(theme, true).tooltip); expect(screen.getByText('0 matches')).toBeDefined(); userEvent.hover(tooltip!); jest.advanceTimersByTime(1000); @@ -114,4 +115,16 @@ describe('', () => { expect(screen.getByText(/0 span matches for the filters selected/)).toBeDefined(); }); }); + + it('renders services, depth correctly', async () => { + const { container } = render(); + const theme = createTheme(); + const tooltip = container.querySelector('.' + getStyles(theme, true).tooltip); + userEvent.hover(tooltip!); + jest.advanceTimersByTime(1000); + await waitFor(() => { + expect(screen.getByText(/Services: 2\/3/)).toBeDefined(); + expect(screen.getByText(/Depth: 1\/1/)).toBeDefined(); + }); + }); }); diff --git a/public/app/features/explore/TraceView/components/TracePageHeader/SearchBar/NextPrevResult.tsx b/public/app/features/explore/TraceView/components/TracePageHeader/SearchBar/NextPrevResult.tsx index 79230e4a76c..98c725b3918 100644 --- a/public/app/features/explore/TraceView/components/TracePageHeader/SearchBar/NextPrevResult.tsx +++ b/public/app/features/explore/TraceView/components/TracePageHeader/SearchBar/NextPrevResult.tsx @@ -13,31 +13,34 @@ // limitations under the License. import { css, cx } from '@emotion/css'; -import React, { memo, Dispatch, SetStateAction, useEffect } from 'react'; +import { get, maxBy, values } from 'lodash'; +import React, { memo, Dispatch, SetStateAction, useEffect, useCallback } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { config, reportInteraction } from '@grafana/runtime'; -import { Icon, Tooltip, useTheme2 } from '@grafana/ui'; +import { Icon, PopoverContent, Tooltip, useTheme2 } from '@grafana/ui'; import { getButtonStyles } from '@grafana/ui/src/components/Button'; +import { Trace } from '../../types'; + export type NextPrevResultProps = { + trace: Trace; spanFilterMatches: Set | undefined; setFocusedSpanIdForSearch: Dispatch>; focusedSpanIndexForSearch: number; setFocusedSpanIndexForSearch: Dispatch>; datasourceType: string; - totalSpans: number; showSpanFilters: boolean; }; export default memo(function NextPrevResult(props: NextPrevResultProps) { const { + trace, spanFilterMatches, setFocusedSpanIdForSearch, focusedSpanIndexForSearch, setFocusedSpanIndexForSearch, datasourceType, - totalSpans, showSpanFilters, } = props; const styles = getStyles(useTheme2(), showSpanFilters); @@ -109,30 +112,88 @@ export default memo(function NextPrevResult(props: NextPrevResultProps) { }; const buttonEnabled = (spanFilterMatches && spanFilterMatches?.size > 0) ?? false; - const amountText = spanFilterMatches?.size === 1 ? 'match' : 'matches'; - const matches = - spanFilterMatches?.size === 0 ? ( - <> - 0 matches - - - + const buttonClass = buttonEnabled ? styles.button : cx(styles.button, styles.buttonDisabled); + + const getTooltip = useCallback( + (content: PopoverContent) => { + return ( + + + - - ) : focusedSpanIndexForSearch !== -1 ? ( - `${focusedSpanIndexForSearch + 1}/${spanFilterMatches?.size} ${amountText}` - ) : ( - `${spanFilterMatches?.size} ${amountText}` - ); - const buttonClass = buttonEnabled ? styles.button : cx(styles.button, styles.buttonDisabled); + ); + }, + [styles.tooltip] + ); + + const getMatchesMetadata = useCallback( + (depth: number, services: number) => { + const matchedServices: string[] = []; + const matchedDepth: number[] = []; + let metadata = ( + <> + {`${trace.spans.length} spans`} + {getTooltip( + <> +
Services: {services}
+
Depth: {depth}
+ + )} + + ); + + if (spanFilterMatches) { + spanFilterMatches.forEach((spanID) => { + matchedServices.push(trace.processes[spanID].serviceName); + matchedDepth.push(trace.spans.find((span) => span.spanID === spanID)?.depth || 0); + }); + + if (spanFilterMatches.size === 0) { + metadata = ( + <> + 0 matches + {getTooltip( + 'There are 0 span matches for the filters selected. Please try removing some of the selected filters.' + )} + + ); + } else { + const type = spanFilterMatches.size === 1 ? 'match' : 'matches'; + const text = + focusedSpanIndexForSearch !== -1 + ? `${focusedSpanIndexForSearch + 1}/${spanFilterMatches.size} ${type}` + : `${spanFilterMatches.size} ${type}`; + + metadata = ( + <> + {text} + {getTooltip( + <> +
+ Services: {new Set(matchedServices).size}/{services} +
+
+ Depth: {new Set(matchedDepth).size}/{depth} +
+ + )} + + ); + } + } + + return metadata; + }, + [focusedSpanIndexForSearch, getTooltip, spanFilterMatches, trace.processes, trace.spans] + ); + + const services = new Set(values(trace.processes).map((p) => p.serviceName)).size; + const depth = get(maxBy(trace.spans, 'depth'), 'depth', 0) + 1; return ( <> - {spanFilterMatches ? matches : `${totalSpans} spans`} + {getMatchesMetadata(depth, services)}
{ matches: css` margin-right: ${theme.spacing(2)}; `, - matchesTooltip: css` + tooltip: css` color: #aaa; - margin: -2px 0 0 10px; + margin: 0 0 0 5px; `, }; }; diff --git a/public/app/features/explore/TraceView/components/TracePageHeader/SpanFilters/SpanFilters.tsx b/public/app/features/explore/TraceView/components/TracePageHeader/SpanFilters/SpanFilters.tsx index 23681adbd37..add4cd20652 100644 --- a/public/app/features/explore/TraceView/components/TracePageHeader/SpanFilters/SpanFilters.tsx +++ b/public/app/features/explore/TraceView/components/TracePageHeader/SpanFilters/SpanFilters.tsx @@ -277,12 +277,12 @@ export const SpanFilters = memo((props: SpanFilterProps) => { {!showSpanFilters && (
@@ -448,7 +448,7 @@ export const SpanFilters = memo((props: SpanFilterProps) => {