diff --git a/web/ui/react-app/src/components/SearchBar.tsx b/web/ui/react-app/src/components/SearchBar.tsx index 761b290684..23aa515ad7 100644 --- a/web/ui/react-app/src/components/SearchBar.tsx +++ b/web/ui/react-app/src/components/SearchBar.tsx @@ -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) => void; + handleChange: (e: string) => void; placeholder: string; + defaultValue: string; } -const SearchBar: FC = ({ handleChange, placeholder }) => { +const SearchBar: FC = ({ handleChange, placeholder, defaultValue }) => { let filterTimeout: NodeJS.Timeout; const handleSearchChange = (e: ChangeEvent) => { clearTimeout(filterTimeout); filterTimeout = setTimeout(() => { - handleChange(e); + handleChange(e.target.value); }, 300); }; + useEffect(() => { + handleChange(defaultValue); + }, [defaultValue, handleChange]); + return ( {} - + ); }; diff --git a/web/ui/react-app/src/pages/alerts/AlertContents.tsx b/web/ui/react-app/src/pages/alerts/AlertContents.tsx index affc39df9d..2837399f7c 100644 --- a/web/ui/react-app/src/pages/alerts/AlertContents.tsx +++ b/web/ui/react-app/src/pages/alerts/AlertContents.tsx @@ -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 = ({ 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 = ({ groups = [], statsCount }) => { }); }; - const handleSearchChange = (e: ChangeEvent) => { - 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 = ({ groups = [], statsCount }) => { })} - + diff --git a/web/ui/react-app/src/pages/serviceDiscovery/Services.tsx b/web/ui/react-app/src/pages/serviceDiscovery/Services.tsx index ea7fd9462c..7664be1f5f 100644 --- a/web/ui/react-app/src/pages/serviceDiscovery/Services.tsx +++ b/web/ui/react-app/src/pages/serviceDiscovery/Services.tsx @@ -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 = ({ activeTargets, dropped const [targetList, setTargetList] = useState(processSummary(activeTargets, droppedTargets)); const [labelList, setLabelList] = useState(processTargets(activeTargets, droppedTargets)); - const handleSearchChange = (e: ChangeEvent) => { - 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 = ({ activeTargets, dropped <>

Service Discovery

- +
    {mapObjEntries(targetList, ([k, v]) => ( diff --git a/web/ui/react-app/src/pages/targets/ScrapePoolList.tsx b/web/ui/react-app/src/pages/targets/ScrapePoolList.tsx index d401f772c9..d7078128e8 100644 --- a/web/ui/react-app/src/pages/targets/ScrapePoolList.tsx +++ b/web/ui/react-app/src/pages/targets/ScrapePoolList.tsx @@ -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 = ({ activeTargets }) => { const [expanded, setExpanded] = useLocalStorage('targets-page-expansion-state', initialExpanded); const { showHealthy, showUnhealthy } = filter; - const handleSearchChange = (e: ChangeEvent) => { - 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 = ({ activeTargets }) => { - + {Object.keys(poolList) diff --git a/web/ui/react-app/src/utils/index.ts b/web/ui/react-app/src/utils/index.ts index ca714970f5..5e05c25e16 100644 --- a/web/ui/react-app/src/utils/index.ts +++ b/web/ui/react-app/src/utils/index.ts @@ -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 = ( o: T, cb: ([k, v]: [string, T[key]], i: number, arr: [string, T[key]][]) => Z