From 3fe21f1ccab0cdcc387be605c1e127b412358eac Mon Sep 17 00:00:00 2001 From: Sonia Aguilar <33540275+soniaAguilarPeiron@users.noreply.github.com> Date: Wed, 23 Jul 2025 11:32:30 +0200 Subject: [PATCH] Alerting: Add pagination for rules in a group in the rule list view v2 (#108436) * add pagination for rules in a group, in the rule list view v2 * update translations * remove gap in stack --- .../unified/rule-list/GrafanaGroupLoader.tsx | 60 +++++++++++++++++-- public/locales/en-US/grafana.json | 3 +- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/public/app/features/alerting/unified/rule-list/GrafanaGroupLoader.tsx b/public/app/features/alerting/unified/rule-list/GrafanaGroupLoader.tsx index 2dee977835d..7d9a8d184de 100644 --- a/public/app/features/alerting/unified/rule-list/GrafanaGroupLoader.tsx +++ b/public/app/features/alerting/unified/rule-list/GrafanaGroupLoader.tsx @@ -1,13 +1,20 @@ +import { css } from '@emotion/css'; +import { useMemo } from 'react'; + +import { GrafanaTheme2 } from '@grafana/data'; import { t } from '@grafana/i18n'; -import { Alert } from '@grafana/ui'; +import { Alert, Pagination, Stack, useStyles2 } from '@grafana/ui'; import { GrafanaRuleGroupIdentifier } from 'app/types/unified-alerting'; import { prometheusApi } from '../api/prometheusApi'; +import { usePagination } from '../hooks/usePagination'; import { RULE_LIST_POLL_INTERVAL_MS } from '../utils/constants'; import { GrafanaRuleListItem } from './GrafanaRuleListItem'; import { AlertRuleListItemSkeleton } from './components/AlertRuleListItemLoader'; +const DEFAULT_PER_PAGE_PAGINATION_RULES_PER_GROUP_LIST_VIEW_V2 = 100; + const { useGetGrafanaGroupsQuery } = prometheusApi; export interface GrafanaGroupLoaderProps { @@ -31,6 +38,8 @@ export function GrafanaGroupLoader({ namespaceName, expectedRulesCount = 3, // 3 is a random number. Usually we get the number of rules from Prometheus response }: GrafanaGroupLoaderProps) { + const styles = useStyles2(getStyles); + const { data: promResponse, isLoading: isPromResponseLoading } = useGetGrafanaGroupsQuery( { folderUid: groupIdentifier.namespace.uid, @@ -40,6 +49,16 @@ export function GrafanaGroupLoader({ { pollingInterval: RULE_LIST_POLL_INTERVAL_MS } ); + const rules = useMemo(() => { + return promResponse?.data.groups.at(0)?.rules ?? []; + }, [promResponse]); + + const { pageItems, page, numberOfPages, onPageChange } = usePagination( + rules, + 1, + DEFAULT_PER_PAGE_PAGINATION_RULES_PER_GROUP_LIST_VIEW_V2 + ); + if (isPromResponseLoading) { return ( <> @@ -63,9 +82,14 @@ export function GrafanaGroupLoader({ ); } + // If no rules found, return early without pagination + if (rules.length === 0) { + return ; + } + return ( - <> - {promResponse.data.groups.at(0)?.rules.map((promRule) => { + + {pageItems.map((promRule) => { return ( ); })} - > + + {numberOfPages > 1 && ( + + )} + + ); } + +const getStyles = (theme: GrafanaTheme2) => ({ + pagination: css({ + display: 'flex', + margin: 0, + paddingTop: theme.spacing(1), + paddingBottom: theme.spacing(0.25), + justifyContent: 'center', + float: 'none', + }), + paginationWrapper: css({ + display: 'flex', + justifyContent: 'flex-start', + alignItems: 'center', + marginLeft: theme.spacing(2.5), + }), +}); diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 2c009a575c4..42438db155f 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -1551,7 +1551,8 @@ "title": "Edit evaluation group" }, "group-loader": { - "group-load-failed": "Failed to load rules from group {{ groupName }} in {{ namespaceName }}" + "group-load-failed": "Failed to load rules from group {{ groupName }} in {{ namespaceName }}", + "no-rules": "No rules found in this group" }, "group-status": { "content-the-group-is-being-deleted": "The group is being deleted"