Alerting: Paginate data source managed rules within groups (#108803)
This commit is contained in:
@@ -27,3 +27,21 @@ export function usePagination<T>(items: T[], initialPage = 1, itemsPerPage: numb
|
||||
|
||||
return { page, onPageChange, numberOfPages, pageItems, pageStart, pageEnd, nextPage, previousPage };
|
||||
}
|
||||
|
||||
export function useContinuousPagination<T>(items: T[], itemsPerPage: number) {
|
||||
const [pageIndex, setPageIndex] = useState(1);
|
||||
|
||||
const hasMore = items.length > itemsPerPage * pageIndex;
|
||||
const pageItems = items.slice(0, itemsPerPage * pageIndex);
|
||||
|
||||
const loadMore = useCallback(() => {
|
||||
setPageIndex((index) => index + 1);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
pageItems,
|
||||
pageIndex,
|
||||
loadMore,
|
||||
hasMore,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { isFetchError } from '@grafana/runtime';
|
||||
import { Alert } from '@grafana/ui';
|
||||
import { Alert, useStyles2 } from '@grafana/ui';
|
||||
import { DataSourceRuleGroupIdentifier } from 'app/types/unified-alerting';
|
||||
import {
|
||||
PromRuleDTO,
|
||||
@@ -16,13 +18,15 @@ import {
|
||||
import { alertRuleApi } from '../api/alertRuleApi';
|
||||
import { featureDiscoveryApi } from '../api/featureDiscoveryApi';
|
||||
import { prometheusApi } from '../api/prometheusApi';
|
||||
import { RULE_LIST_POLL_INTERVAL_MS } from '../utils/constants';
|
||||
import { useContinuousPagination } from '../hooks/usePagination';
|
||||
import { DEFAULT_PER_PAGE_PAGINATION_RULES_PER_GROUP, RULE_LIST_POLL_INTERVAL_MS } from '../utils/constants';
|
||||
import { hashRule } from '../utils/rule-id';
|
||||
import { getRuleName, isCloudRulerGroup } from '../utils/rules';
|
||||
|
||||
import { DataSourceRuleListItem } from './DataSourceRuleListItem';
|
||||
import { RuleOperationListItem } from './components/AlertRuleListItem';
|
||||
import { AlertRuleListItemSkeleton } from './components/AlertRuleListItemLoader';
|
||||
import { LoadMoreButton } from './components/LoadMoreButton';
|
||||
import { RuleActionsButtons } from './components/RuleActionsButtons.V2';
|
||||
import { RuleOperation } from './components/RuleListIcon';
|
||||
import { matchRulesGroup } from './ruleMatching';
|
||||
@@ -174,15 +178,21 @@ export function RulerBasedGroupRules({
|
||||
promGroup,
|
||||
rulerGroup,
|
||||
}: RulerBasedGroupRulesProps) {
|
||||
const styles = useStyles2(getStyles);
|
||||
const { namespace, groupName } = groupIdentifier;
|
||||
|
||||
const { matches, promOnlyRules } = useMemo(() => {
|
||||
return matchRulesGroup(rulerGroup, promGroup);
|
||||
}, [promGroup, rulerGroup]);
|
||||
|
||||
const { pageItems, hasMore, loadMore } = useContinuousPagination(
|
||||
rulerGroup.rules,
|
||||
DEFAULT_PER_PAGE_PAGINATION_RULES_PER_GROUP
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{rulerGroup.rules.map((rulerRule) => {
|
||||
{pageItems.map((rulerRule) => {
|
||||
const promRule = matches.get(rulerRule);
|
||||
|
||||
return promRule ? (
|
||||
@@ -222,6 +232,18 @@ export function RulerBasedGroupRules({
|
||||
showLocation={false}
|
||||
/>
|
||||
))}
|
||||
{hasMore && (
|
||||
<li aria-selected="false" role="treeitem" className={styles.loadMoreWrapper}>
|
||||
<LoadMoreButton onClick={loadMore} />
|
||||
</li>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
loadMoreWrapper: css({
|
||||
listStyle: 'none',
|
||||
paddingTop: theme.spacing(1),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -3,17 +3,16 @@ import { useMemo } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { Alert, Pagination, Stack, useStyles2 } from '@grafana/ui';
|
||||
import { Alert, 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 { useContinuousPagination } from '../hooks/usePagination';
|
||||
import { DEFAULT_PER_PAGE_PAGINATION_RULES_PER_GROUP, 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;
|
||||
import { LoadMoreButton } from './components/LoadMoreButton';
|
||||
|
||||
const { useGetGrafanaGroupsQuery } = prometheusApi;
|
||||
|
||||
@@ -53,11 +52,7 @@ export function GrafanaGroupLoader({
|
||||
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
|
||||
);
|
||||
const { pageItems, hasMore, loadMore } = useContinuousPagination(rules, DEFAULT_PER_PAGE_PAGINATION_RULES_PER_GROUP);
|
||||
|
||||
if (isPromResponseLoading) {
|
||||
return (
|
||||
@@ -101,34 +96,18 @@ export function GrafanaGroupLoader({
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<div className={styles.paginationWrapper}>
|
||||
{numberOfPages > 1 && (
|
||||
<Pagination
|
||||
currentPage={page}
|
||||
numberOfPages={numberOfPages}
|
||||
onNavigate={onPageChange}
|
||||
hideWhenSinglePage
|
||||
className={styles.pagination}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{hasMore && (
|
||||
<li aria-selected="false" role="treeitem" className={styles.loadMoreWrapper}>
|
||||
<LoadMoreButton onClick={loadMore} />
|
||||
</li>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
pagination: css({
|
||||
display: 'flex',
|
||||
margin: 0,
|
||||
loadMoreWrapper: css({
|
||||
listStyle: 'none',
|
||||
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),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -65,6 +65,8 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
||||
// unfortunately we have to resort to this since we can't overwrite the styles of the list items individually
|
||||
// unless we clone the React Elements and modify className
|
||||
'li[role=treeitem]': {
|
||||
listStyle: 'none',
|
||||
position: 'relative',
|
||||
paddingLeft: theme.spacing(6.5),
|
||||
|
||||
'&:before': {
|
||||
|
||||
@@ -8,6 +8,8 @@ export const SILENCES_POLL_INTERVAL_MS = 20000;
|
||||
export const NOTIFICATIONS_POLL_INTERVAL_MS = 20000;
|
||||
export const CONTACT_POINTS_STATE_INTERVAL_MS = 20000;
|
||||
|
||||
export const DEFAULT_PER_PAGE_PAGINATION_RULES_PER_GROUP = 100;
|
||||
|
||||
export const TIMESERIES = 'timeseries';
|
||||
export const TABLE = 'table';
|
||||
export const STAT = 'stat';
|
||||
|
||||
Reference in New Issue
Block a user