mirror of
https://github.com/prometheus/prometheus.git
synced 2025-01-26 13:11:11 -08:00
Allow setting the search term on alerts, targets, and discovery pages via the URL (#10613)
* changes in files of the expr in alerts, service and targets, Signed-off-by: AndreSPy1 <elsenorcito@gmail.com> Signed-off-by: AndreSPy1 <elsenorcito@gmail.com> * update prettier corrections Signed-off-by: AndreSPy1 <elsenorcito@gmail.com> * update prettier corrections Signed-off-by: AndreSPy1 <elsenorcito@gmail.com> * correction suggested fixes PR Signed-off-by: AndreSPy1 <elsenorcito@gmail.com> * Update Snap AlertContents.test.tsx and update recommendations in the code Signed-off-by: AndreSPy1 <elsenorcito@gmail.com> * Update web/ui/react-app/src/components/SearchBar.tsx exactly, thanks :) Co-authored-by: Julius Volz <julius.volz@gmail.com> Signed-off-by: AndreSPy1 <elsenorcito@gmail.com> * Revert "Update web/ui/react-app/src/components/SearchBar.tsx " This reverts commit 679c763a02f65297e3f761db372a0928550f288c. Signed-off-by: AndreSPy1 <elsenorcito@gmail.com> * update SearchBar component Signed-off-by: AndreSPy1 <elsenorcito@gmail.com> * eslint-disable update SearchBar testing failed Signed-off-by: AndreSPy1 <elsenorcito@gmail.com> * correction part in eslint-disable Signed-off-by: AndreSPy1 <elsenorcito@gmail.com> * fully corrected suggestion with search expression in url Signed-off-by: AndreSPy1 <elsenorcito@gmail.com> * implementation in handleSearchChange with useCallBack Signed-off-by: AndreSPy1 <elsenorcito@gmail.com> Co-authored-by: Julius Volz <julius.volz@gmail.com>
This commit is contained in:
parent
685493187e
commit
d8ca9aa67b
|
@ -1,29 +1,34 @@
|
|||
import React, { ChangeEvent, FC } from 'react';
|
||||
import React, { ChangeEvent, FC, useEffect } from 'react';
|
||||
import { Input, InputGroup, InputGroupAddon, InputGroupText } from 'reactstrap';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faSearch } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
export interface SearchBarProps {
|
||||
handleChange: (e: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => void;
|
||||
handleChange: (e: string) => void;
|
||||
placeholder: string;
|
||||
defaultValue: string;
|
||||
}
|
||||
|
||||
const SearchBar: FC<SearchBarProps> = ({ handleChange, placeholder }) => {
|
||||
const SearchBar: FC<SearchBarProps> = ({ handleChange, placeholder, defaultValue }) => {
|
||||
let filterTimeout: NodeJS.Timeout;
|
||||
|
||||
const handleSearchChange = (e: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
|
||||
clearTimeout(filterTimeout);
|
||||
filterTimeout = setTimeout(() => {
|
||||
handleChange(e);
|
||||
handleChange(e.target.value);
|
||||
}, 300);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
handleChange(defaultValue);
|
||||
}, [defaultValue, handleChange]);
|
||||
|
||||
return (
|
||||
<InputGroup>
|
||||
<InputGroupAddon addonType="prepend">
|
||||
<InputGroupText>{<FontAwesomeIcon icon={faSearch} />}</InputGroupText>
|
||||
</InputGroupAddon>
|
||||
<Input autoFocus onChange={handleSearchChange} placeholder={placeholder} />
|
||||
<Input autoFocus onChange={handleSearchChange} placeholder={placeholder} defaultValue={defaultValue} />
|
||||
</InputGroup>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React, { ChangeEvent, FC, Fragment, useEffect, useState } from 'react';
|
||||
import React, { FC, Fragment, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Badge, Col, Row } from 'reactstrap';
|
||||
import CollapsibleAlertPanel from './CollapsibleAlertPanel';
|
||||
import Checkbox from '../../components/Checkbox';
|
||||
import { isPresent } from '../../utils';
|
||||
import { getQuerySearchFilter, isPresent, setQuerySearchFilter } from '../../utils';
|
||||
import { Rule } from '../../types/types';
|
||||
import { useLocalStorage } from '../../hooks/useLocalStorage';
|
||||
import CustomInfiniteScroll, { InfiniteScrollItemsProps } from '../../components/CustomInfiniteScroll';
|
||||
|
@ -71,7 +71,6 @@ const AlertsContent: FC<AlertsProps> = ({ groups = [], statsCount }) => {
|
|||
inactive: true,
|
||||
});
|
||||
const [showAnnotations, setShowAnnotations] = useLocalStorage('alerts-annotations-status', { checked: false });
|
||||
|
||||
const toggleFilter = (ruleState: RuleState) => () => {
|
||||
setFilter({
|
||||
...filter,
|
||||
|
@ -79,26 +78,32 @@ const AlertsContent: FC<AlertsProps> = ({ groups = [], statsCount }) => {
|
|||
});
|
||||
};
|
||||
|
||||
const handleSearchChange = (e: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
|
||||
if (e.target.value !== '') {
|
||||
const pattern = e.target.value.trim();
|
||||
const result: RuleGroup[] = [];
|
||||
for (const group of groups) {
|
||||
const ruleFilterList = kvSearchRule.filter(pattern, group.rules);
|
||||
if (ruleFilterList.length > 0) {
|
||||
result.push({
|
||||
file: group.file,
|
||||
name: group.name,
|
||||
interval: group.interval,
|
||||
rules: ruleFilterList.map((value) => value.original),
|
||||
});
|
||||
const handleSearchChange = useCallback(
|
||||
(value: string) => {
|
||||
setQuerySearchFilter(value);
|
||||
if (value !== '') {
|
||||
const pattern = value.trim();
|
||||
const result: RuleGroup[] = [];
|
||||
for (const group of groups) {
|
||||
const ruleFilterList = kvSearchRule.filter(pattern, group.rules);
|
||||
if (ruleFilterList.length > 0) {
|
||||
result.push({
|
||||
file: group.file,
|
||||
name: group.name,
|
||||
interval: group.interval,
|
||||
rules: ruleFilterList.map((value) => value.original),
|
||||
});
|
||||
}
|
||||
}
|
||||
setGroupList(result);
|
||||
} else {
|
||||
setGroupList(groups);
|
||||
}
|
||||
setGroupList(result);
|
||||
} else {
|
||||
setGroupList(groups);
|
||||
}
|
||||
};
|
||||
},
|
||||
[groups]
|
||||
);
|
||||
|
||||
const defaultValue = useMemo(getQuerySearchFilter, []);
|
||||
|
||||
useEffect(() => {
|
||||
const result: RuleGroup[] = [];
|
||||
|
@ -131,7 +136,7 @@ const AlertsContent: FC<AlertsProps> = ({ groups = [], statsCount }) => {
|
|||
})}
|
||||
</Col>
|
||||
<Col lg="5" md="4">
|
||||
<SearchBar handleChange={handleSearchChange} placeholder="Filter by name or labels" />
|
||||
<SearchBar defaultValue={defaultValue} handleChange={handleSearchChange} placeholder="Filter by name or labels" />
|
||||
</Col>
|
||||
<Col className="d-flex flex-row-reverse" md="3">
|
||||
<Checkbox
|
||||
|
|
|
@ -100,6 +100,7 @@ exports[`AlertsContent matches a snapshot 1`] = `
|
|||
}
|
||||
>
|
||||
<SearchBar
|
||||
defaultValue=""
|
||||
handleChange={[Function]}
|
||||
placeholder="Filter by name or labels"
|
||||
/>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import React, { ChangeEvent, FC, useEffect, useState } from 'react';
|
||||
import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useFetch } from '../../hooks/useFetch';
|
||||
import { LabelsTable } from './LabelsTable';
|
||||
import { DroppedTarget, Labels, Target } from '../targets/target';
|
||||
|
||||
import { withStatusIndicator } from '../../components/withStatusIndicator';
|
||||
import { mapObjEntries } from '../../utils';
|
||||
import { setQuerySearchFilter, mapObjEntries, getQuerySearchFilter } from '../../utils';
|
||||
import { usePathPrefix } from '../../contexts/PathPrefixContext';
|
||||
import { API_PATH } from '../../constants/constants';
|
||||
import { KVSearch } from '@nexucis/kvsearch';
|
||||
|
@ -94,14 +94,20 @@ export const ServiceDiscoveryContent: FC<ServiceMap> = ({ activeTargets, dropped
|
|||
const [targetList, setTargetList] = useState(processSummary(activeTargets, droppedTargets));
|
||||
const [labelList, setLabelList] = useState(processTargets(activeTargets, droppedTargets));
|
||||
|
||||
const handleSearchChange = (e: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
|
||||
if (e.target.value !== '') {
|
||||
const result = kvSearch.filter(e.target.value.trim(), activeTargets);
|
||||
setActiveTargetList(result.map((value) => value.original));
|
||||
} else {
|
||||
setActiveTargetList(activeTargets);
|
||||
}
|
||||
};
|
||||
const handleSearchChange = useCallback(
|
||||
(value: string) => {
|
||||
setQuerySearchFilter(value);
|
||||
if (value !== '') {
|
||||
const result = kvSearch.filter(value.trim(), activeTargets);
|
||||
setActiveTargetList(result.map((value) => value.original));
|
||||
} else {
|
||||
setActiveTargetList(activeTargets);
|
||||
}
|
||||
},
|
||||
[activeTargets]
|
||||
);
|
||||
|
||||
const defaultValue = useMemo(getQuerySearchFilter, []);
|
||||
|
||||
useEffect(() => {
|
||||
setTargetList(processSummary(activeTargetList, droppedTargets));
|
||||
|
@ -112,7 +118,7 @@ export const ServiceDiscoveryContent: FC<ServiceMap> = ({ activeTargets, dropped
|
|||
<>
|
||||
<h2>Service Discovery</h2>
|
||||
<Container>
|
||||
<SearchBar handleChange={handleSearchChange} placeholder="Filter by labels" />
|
||||
<SearchBar defaultValue={defaultValue} handleChange={handleSearchChange} placeholder="Filter by labels" />
|
||||
</Container>
|
||||
<ul>
|
||||
{mapObjEntries(targetList, ([k, v]) => (
|
||||
|
|
|
@ -4,7 +4,7 @@ import { useFetch } from '../../hooks/useFetch';
|
|||
import { API_PATH } from '../../constants/constants';
|
||||
import { groupTargets, ScrapePool, ScrapePools, Target } from './target';
|
||||
import { withStatusIndicator } from '../../components/withStatusIndicator';
|
||||
import { ChangeEvent, FC, useEffect, useState } from 'react';
|
||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Col, Collapse, Row } from 'reactstrap';
|
||||
import { ScrapePoolContent } from './ScrapePoolContent';
|
||||
import Filter, { Expanded, FilterData } from './Filter';
|
||||
|
@ -12,6 +12,7 @@ import { useLocalStorage } from '../../hooks/useLocalStorage';
|
|||
import styles from './ScrapePoolPanel.module.css';
|
||||
import { ToggleMoreLess } from '../../components/ToggleMoreLess';
|
||||
import SearchBar from '../../components/SearchBar';
|
||||
import { setQuerySearchFilter, getQuerySearchFilter } from '../../utils/index';
|
||||
|
||||
interface ScrapePoolListProps {
|
||||
activeTargets: Target[];
|
||||
|
@ -72,14 +73,20 @@ const ScrapePoolListContent: FC<ScrapePoolListProps> = ({ activeTargets }) => {
|
|||
const [expanded, setExpanded] = useLocalStorage('targets-page-expansion-state', initialExpanded);
|
||||
const { showHealthy, showUnhealthy } = filter;
|
||||
|
||||
const handleSearchChange = (e: ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
|
||||
if (e.target.value !== '') {
|
||||
const result = kvSearch.filter(e.target.value.trim(), activeTargets);
|
||||
setTargetList(result.map((value) => value.original));
|
||||
} else {
|
||||
setTargetList(activeTargets);
|
||||
}
|
||||
};
|
||||
const handleSearchChange = useCallback(
|
||||
(value: string) => {
|
||||
setQuerySearchFilter(value);
|
||||
if (value !== '') {
|
||||
const result = kvSearch.filter(value.trim(), activeTargets);
|
||||
setTargetList(result.map((value) => value.original));
|
||||
} else {
|
||||
setTargetList(activeTargets);
|
||||
}
|
||||
},
|
||||
[activeTargets]
|
||||
);
|
||||
|
||||
const defaultValue = useMemo(getQuerySearchFilter, []);
|
||||
|
||||
useEffect(() => {
|
||||
const list = targetList.filter((t) => showHealthy || t.health.toLowerCase() !== 'up');
|
||||
|
@ -93,7 +100,11 @@ const ScrapePoolListContent: FC<ScrapePoolListProps> = ({ activeTargets }) => {
|
|||
<Filter filter={filter} setFilter={setFilter} expanded={expanded} setExpanded={setExpanded} />
|
||||
</Col>
|
||||
<Col xs="6">
|
||||
<SearchBar handleChange={handleSearchChange} placeholder="Filter by endpoint or labels" />
|
||||
<SearchBar
|
||||
defaultValue={defaultValue}
|
||||
handleChange={handleSearchChange}
|
||||
placeholder="Filter by endpoint or labels"
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
{Object.keys(poolList)
|
||||
|
|
|
@ -243,9 +243,21 @@ export const encodePanelOptionsToQueryString = (panels: PanelMeta[]): string =>
|
|||
return `?${panels.map(toQueryString).join('&')}`;
|
||||
};
|
||||
|
||||
export const setQuerySearchFilter = (search: string) => {
|
||||
window.history.pushState({}, '', `?search=${search}`);
|
||||
};
|
||||
|
||||
export const getQuerySearchFilter = (): string => {
|
||||
const locationSearch = window.location.search;
|
||||
const params = new URLSearchParams(locationSearch);
|
||||
const search = params.get('search') || '';
|
||||
return search;
|
||||
};
|
||||
|
||||
export const createExpressionLink = (expr: string): string => {
|
||||
return `../graph?g0.expr=${encodeURIComponent(expr)}&g0.tab=1&g0.stacked=0&g0.show_exemplars=0.g0.range_input=1h.`;
|
||||
};
|
||||
|
||||
export const mapObjEntries = <T, key extends keyof T, Z>(
|
||||
o: T,
|
||||
cb: ([k, v]: [string, T[key]], i: number, arr: [string, T[key]][]) => Z
|
||||
|
|
Loading…
Reference in a new issue