From 3cc598f65b19908f3fdf78ceb375b1cdbf25293a Mon Sep 17 00:00:00 2001 From: Konrad Lalik Date: Tue, 22 Feb 2022 13:37:47 +0100 Subject: [PATCH] Add label filtering by clicking a tag --- .../alerting/unified/AlertsFolderView.tsx | 38 ++++++++----------- .../alerting/unified/hooks/usePagination.ts | 19 ++++++++++ .../alerting/unified/utils/alertmanager.ts | 11 ++++++ 3 files changed, 45 insertions(+), 23 deletions(-) create mode 100644 public/app/features/alerting/unified/hooks/usePagination.ts diff --git a/public/app/features/alerting/unified/AlertsFolderView.tsx b/public/app/features/alerting/unified/AlertsFolderView.tsx index b07d98c5e6a..51a5a78937f 100644 --- a/public/app/features/alerting/unified/AlertsFolderView.tsx +++ b/public/app/features/alerting/unified/AlertsFolderView.tsx @@ -3,13 +3,15 @@ import { GrafanaTheme2 } from '@grafana/data'; import { Stack } from '@grafana/experimental'; import { Card, FilterInput, Icon, Pagination, TagList, useStyles2 } from '@grafana/ui'; import { FolderState } from 'app/types'; +import { isEqual, uniqWith } from 'lodash'; import React, { useEffect, useState } from 'react'; import { useDispatch } from 'react-redux'; import { useDebounce } from 'react-use'; import { useCombinedRuleNamespaces } from './hooks/useCombinedRuleNamespaces'; +import { usePagination } from './hooks/usePagination'; import { useURLSearchParams } from './hooks/useURLSearchParams'; -import { fetchAllPromAndRulerRulesAction, fetchPromRulesAction, fetchRulerRulesAction } from './state/actions'; -import { labelsMatchMatchers, parseMatchers } from './utils/alertmanager'; +import { fetchPromRulesAction, fetchRulerRulesAction } from './state/actions'; +import { labelsMatchMatchers, matchersToString, parseMatcher, parseMatchers } from './utils/alertmanager'; import { GRAFANA_RULES_SOURCE_NAME } from './utils/datasource'; import { createViewLink } from './utils/misc'; @@ -42,6 +44,13 @@ export const AlertsFolderView = ({ folder }: Props) => { const showNoResultsText = alertRules.length === 0 || filteredRules.length === 0; const { page, numberOfPages, onPageChange, pageItems } = usePagination(filteredRules, 1, ITEMS_PER_PAGE); + const onTagClick = (tagName: string) => { + const tagMatcherField = parseMatcher(tagName); + const uniqueMatchers = uniqWith([...matchers, tagMatcherField], isEqual); + const matchersString = matchersToString(uniqueMatchers); + setLabelFilter(matchersString); + }; + return ( @@ -71,6 +80,7 @@ export const AlertsFolderView = ({ folder }: Props) => { {currentRule.name} `${labelKey}=${currentRule.labels[labelKey]}`)} /> @@ -110,8 +120,8 @@ function useAlertsFolderViewFilters() { () => setSearchParams( { - [AlertFolderViewFilters.nameFilter]: getNotEmptyStringOrUndefined(nameFilter), - [AlertFolderViewFilters.labelFilter]: getNotEmptyStringOrUndefined(labelFilter), + [AlertFolderViewFilters.nameFilter]: getNonEmptyStringOrUndefined(nameFilter), + [AlertFolderViewFilters.labelFilter]: getNonEmptyStringOrUndefined(labelFilter), }, true ), @@ -129,28 +139,10 @@ function useAlertsFolderViewFilters() { return { nameFilter, labelFilter, setNameFilter, setLabelFilter }; } -function getNotEmptyStringOrUndefined(value: string | undefined | null) { +function getNonEmptyStringOrUndefined(value: string | undefined | null) { return value || undefined; } -function usePagination(items: T[], initialPage: number, itemsPerPage: number) { - const [page, setPage] = useState(initialPage); - - const numberOfPages = Math.ceil(items.length / itemsPerPage); - - const firstItemOnPageIndex = itemsPerPage * (page - 1); - const pageItems = items.slice(firstItemOnPageIndex, firstItemOnPageIndex + itemsPerPage); - - const onPageChange = (newPage: number) => { - setPage(newPage); - }; - - // Reset the current page when number of changes has been changed - useEffect(() => setPage(1), [numberOfPages]); - - return { page, onPageChange, numberOfPages, pageItems }; -} - export const getStyles = (theme: GrafanaTheme2) => ({ card: css` grid-template-columns: auto 1fr 2fr; diff --git a/public/app/features/alerting/unified/hooks/usePagination.ts b/public/app/features/alerting/unified/hooks/usePagination.ts new file mode 100644 index 00000000000..f5fc76fe15e --- /dev/null +++ b/public/app/features/alerting/unified/hooks/usePagination.ts @@ -0,0 +1,19 @@ +import { useEffect, useState } from 'react'; + +export function usePagination(items: T[], initialPage: number, itemsPerPage: number) { + const [page, setPage] = useState(initialPage); + + const numberOfPages = Math.ceil(items.length / itemsPerPage); + + const firstItemOnPageIndex = itemsPerPage * (page - 1); + const pageItems = items.slice(firstItemOnPageIndex, firstItemOnPageIndex + itemsPerPage); + + const onPageChange = (newPage: number) => { + setPage(newPage); + }; + + // Reset the current page when number of changes has been changed + useEffect(() => setPage(1), [numberOfPages]); + + return { page, onPageChange, numberOfPages, pageItems }; +} diff --git a/public/app/features/alerting/unified/utils/alertmanager.ts b/public/app/features/alerting/unified/utils/alertmanager.ts index cfa9a963f44..72b6d101695 100644 --- a/public/app/features/alerting/unified/utils/alertmanager.ts +++ b/public/app/features/alerting/unified/utils/alertmanager.ts @@ -104,6 +104,17 @@ export function matcherFieldToMatcher(field: MatcherFieldValue): Matcher { }; } +export function matchersToString(matchers: Matcher[]) { + const matcherFields = matchers.map(matcherToMatcherField); + + const combinedMatchers = matcherFields.reduce((acc, current) => { + const currentMatcherString = `${current.name}${current.operator}"${current.value}"`; + return acc ? `${acc},${currentMatcherString}"` : currentMatcherString; + }, ''); + + return `{${combinedMatchers}}`; +} + export const matcherFieldOptions: SelectableValue[] = [ { label: MatcherOperator.equal, description: 'Equals', value: MatcherOperator.equal }, { label: MatcherOperator.notEqual, description: 'Does not equal', value: MatcherOperator.notEqual },