mirror of
https://github.com/grafana/grafana.git
synced 2026-09-05 04:40:13 -05:00
* Next/prev text * Tests * Update test * Updated state vars and also made other improvements
222 lines
6.5 KiB
TypeScript
222 lines
6.5 KiB
TypeScript
// Copyright (c) 2018 Uber Technologies, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
import { css } from '@emotion/css';
|
|
import React, { memo, Dispatch, SetStateAction, useEffect, useMemo, useState } from 'react';
|
|
|
|
import { config, reportInteraction } from '@grafana/runtime';
|
|
import { Button, Icon, Switch, Tooltip, useStyles2 } from '@grafana/ui';
|
|
|
|
import { SearchProps } from '../../useSearch';
|
|
import { convertTimeFilter } from '../utils/filter-spans';
|
|
|
|
export type TracePageSearchBarProps = {
|
|
search: SearchProps;
|
|
spanFilterMatches: Set<string> | undefined;
|
|
showSpanFilterMatchesOnly: boolean;
|
|
setShowSpanFilterMatchesOnly: (showMatchesOnly: boolean) => void;
|
|
setFocusedSpanIdForSearch: Dispatch<SetStateAction<string>>;
|
|
datasourceType: string;
|
|
reset: () => void;
|
|
totalSpans: number;
|
|
};
|
|
|
|
export default memo(function NewTracePageSearchBar(props: TracePageSearchBarProps) {
|
|
const {
|
|
search,
|
|
spanFilterMatches,
|
|
showSpanFilterMatchesOnly,
|
|
setShowSpanFilterMatchesOnly,
|
|
setFocusedSpanIdForSearch,
|
|
datasourceType,
|
|
reset,
|
|
totalSpans,
|
|
} = props;
|
|
const [currentSpanIndex, setCurrentSpanIndex] = useState(-1);
|
|
const styles = useStyles2(getStyles);
|
|
|
|
useEffect(() => {
|
|
setCurrentSpanIndex(-1);
|
|
setFocusedSpanIdForSearch('');
|
|
}, [setFocusedSpanIdForSearch, spanFilterMatches]);
|
|
|
|
useEffect(() => {
|
|
if (spanFilterMatches) {
|
|
const spanMatches = Array.from(spanFilterMatches!);
|
|
setFocusedSpanIdForSearch(spanMatches[currentSpanIndex]);
|
|
}
|
|
}, [currentSpanIndex, setFocusedSpanIdForSearch, spanFilterMatches]);
|
|
|
|
const nextResult = () => {
|
|
reportInteraction('grafana_traces_trace_view_find_next_prev_clicked', {
|
|
datasourceType: datasourceType,
|
|
grafana_version: config.buildInfo.version,
|
|
direction: 'next',
|
|
});
|
|
|
|
// new query || at end, go to start
|
|
if (currentSpanIndex === -1 || (spanFilterMatches && currentSpanIndex === spanFilterMatches.size - 1)) {
|
|
setCurrentSpanIndex(0);
|
|
return;
|
|
}
|
|
|
|
// get next
|
|
setCurrentSpanIndex(currentSpanIndex + 1);
|
|
};
|
|
|
|
const prevResult = () => {
|
|
reportInteraction('grafana_traces_trace_view_find_next_prev_clicked', {
|
|
datasourceType: datasourceType,
|
|
grafana_version: config.buildInfo.version,
|
|
direction: 'prev',
|
|
});
|
|
|
|
// new query || at start, go to end
|
|
if (spanFilterMatches && (currentSpanIndex === -1 || currentSpanIndex === 0)) {
|
|
setCurrentSpanIndex(spanFilterMatches.size - 1);
|
|
return;
|
|
}
|
|
|
|
// get prev
|
|
setCurrentSpanIndex(currentSpanIndex - 1);
|
|
};
|
|
|
|
const buttonEnabled = spanFilterMatches && spanFilterMatches?.size > 0;
|
|
const resetEnabled = useMemo(() => {
|
|
return (
|
|
(search.serviceName && search.serviceName !== '') ||
|
|
(search.spanName && search.spanName !== '') ||
|
|
convertTimeFilter(search.from || '') ||
|
|
convertTimeFilter(search.to || '') ||
|
|
search.tags.length > 1 ||
|
|
search.tags.some((tag) => {
|
|
return tag.key;
|
|
})
|
|
);
|
|
}, [search.serviceName, search.spanName, search.from, search.to, search.tags]);
|
|
|
|
const amountText = spanFilterMatches?.size === 1 ? 'match' : 'matches';
|
|
const matches =
|
|
spanFilterMatches?.size === 0 ? (
|
|
<>
|
|
<span>0 matches</span>
|
|
<Tooltip
|
|
content="There are 0 span matches for the filters selected. Please try removing some of the selected filters."
|
|
placement="left"
|
|
>
|
|
<span className={styles.matchesTooltip}>
|
|
<Icon name="info-circle" size="lg" />
|
|
</span>
|
|
</Tooltip>
|
|
</>
|
|
) : currentSpanIndex !== -1 ? (
|
|
`${currentSpanIndex + 1}/${spanFilterMatches?.size} ${amountText}`
|
|
) : (
|
|
`${spanFilterMatches?.size} ${amountText}`
|
|
);
|
|
|
|
return (
|
|
<div className={styles.searchBar}>
|
|
<div className={styles.buttons}>
|
|
<>
|
|
<div className={styles.resetButton}>
|
|
<Button
|
|
variant="destructive"
|
|
disabled={!resetEnabled}
|
|
type="button"
|
|
fill="outline"
|
|
aria-label="Reset filters button"
|
|
onClick={reset}
|
|
>
|
|
Reset
|
|
</Button>
|
|
<div className={styles.matchesOnly}>
|
|
<Switch
|
|
value={showSpanFilterMatchesOnly}
|
|
onChange={(value) => setShowSpanFilterMatchesOnly(value.currentTarget.checked ?? false)}
|
|
label="Show matches only switch"
|
|
/>
|
|
<span onClick={() => setShowSpanFilterMatchesOnly(!showSpanFilterMatchesOnly)}>Show matches only</span>
|
|
</div>
|
|
</div>
|
|
<div className={styles.nextPrevButtons}>
|
|
<span className={styles.matches}>{spanFilterMatches ? matches : `${totalSpans} spans`}</span>
|
|
<Button
|
|
variant="secondary"
|
|
disabled={!buttonEnabled}
|
|
type="button"
|
|
fill="outline"
|
|
aria-label="Prev result button"
|
|
onClick={prevResult}
|
|
>
|
|
Prev
|
|
</Button>
|
|
<Button
|
|
variant="secondary"
|
|
disabled={!buttonEnabled}
|
|
type="button"
|
|
fill="outline"
|
|
aria-label="Next result button"
|
|
onClick={nextResult}
|
|
>
|
|
Next
|
|
</Button>
|
|
</div>
|
|
</>
|
|
</div>
|
|
</div>
|
|
);
|
|
});
|
|
|
|
export const getStyles = () => {
|
|
return {
|
|
searchBar: css`
|
|
display: inline;
|
|
`,
|
|
matchesOnly: css`
|
|
display: inline-flex;
|
|
margin: 0 0 0 10px;
|
|
vertical-align: middle;
|
|
|
|
span {
|
|
cursor: pointer;
|
|
margin: -3px 0 0 5px;
|
|
}
|
|
`,
|
|
buttons: css`
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
margin: 5px 0 0 0;
|
|
`,
|
|
resetButton: css`
|
|
order: 1;
|
|
`,
|
|
nextPrevButtons: css`
|
|
margin-left: auto;
|
|
order: 2;
|
|
|
|
button {
|
|
margin-left: 8px;
|
|
}
|
|
`,
|
|
matches: css`
|
|
margin-right: 5px;
|
|
`,
|
|
matchesTooltip: css`
|
|
color: #aaa;
|
|
margin: -2px 0 0 10px;
|
|
`,
|
|
};
|
|
};
|