Alerting: Add refresh button to contact points selector in simplified routing section. (#80748)

* Add refresh button for contact points selector in simplified routing section

* Clear timeout when unmounting component

* Fix timeout not being correclty removed when component unmounts

* Update css field name

* Kepp loading spinner if refetching receivers takes more than one second

* Fix test snapshot in useContactPointsWithStatus hook

* refactor how we wait for the request response and the timeout to finish
This commit is contained in:
Sonia Aguilar 2024-01-25 08:53:54 +01:00 committed by GitHub
parent ebe8c005ce
commit 6218e28ee9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 49 additions and 3 deletions

View File

@ -144,5 +144,6 @@ exports[`useContactPoints should return contact points with status 1`] = `
],
"error": undefined,
"isLoading": false,
"refetchReceivers": [Function],
}
`;

View File

@ -97,6 +97,7 @@ export function useContactPointsWithStatus() {
error,
isLoading,
contactPoints,
refetchReceivers: fetchAlertmanagerConfiguration.refetch,
};
}

View File

@ -1,8 +1,8 @@
import { css } from '@emotion/css';
import { css, cx } from '@emotion/css';
import React, { useState } from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { Alert, CollapsableSection, LoadingPlaceholder, Stack, TextLink, useStyles2 } from '@grafana/ui';
import { Alert, CollapsableSection, IconButton, LoadingPlaceholder, Stack, TextLink, useStyles2 } from '@grafana/ui';
import { AlertManagerDataSource } from 'app/features/alerting/unified/utils/datasource';
import { createUrl } from 'app/features/alerting/unified/utils/url';
@ -18,15 +18,32 @@ interface AlertManagerManualRoutingProps {
alertManager: AlertManagerDataSource;
}
const LOADING_SPINNER_DURATION = 1000;
export function AlertManagerManualRouting({ alertManager }: AlertManagerManualRoutingProps) {
const styles = useStyles2(getStyles);
const alertManagerName = alertManager.name;
const { isLoading, error: errorInContactPointStatus, contactPoints } = useContactPointsWithStatus();
const { isLoading, error: errorInContactPointStatus, contactPoints, refetchReceivers } = useContactPointsWithStatus();
const [selectedContactPointWithMetadata, setSelectedContactPointWithMetadata] = useState<
ContactPointWithMetadata | undefined
>();
// We need to provide a fake loading state for the contact points, because it might be that the response is so fast that the loading spinner is not shown,
// and the user might think that the contact points are not fetched.
// We will show the loading spinner for 1 second, and if the fetching takes more than 1 second, we will show the loading spinner until the fetching is done.
const [loadingContactPoints, setLoadingContactPoints] = useState(false);
// we need to keep track if the fetching takes more than 1 second, so we can show the loading spinner until the fetching is done
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
const onClickRefresh = () => {
setLoadingContactPoints(true);
Promise.all([refetchReceivers(), sleep(LOADING_SPINNER_DURATION)]).finally(() => {
setLoadingContactPoints(false);
});
};
if (errorInContactPointStatus) {
return <Alert title="Failed to fetch contact points" severity="error" />;
}
@ -51,6 +68,15 @@ export function AlertManagerManualRouting({ alertManager }: AlertManagerManualRo
onSelectContactPoint={setSelectedContactPointWithMetadata}
/>
<div className={styles.contactPointsInfo}>
<IconButton
name="sync"
onClick={onClickRefresh}
aria-label="Refresh contact points"
tooltip="Refresh contact points list"
className={cx(styles.refreshButton, {
[styles.loading]: loadingContactPoints,
})}
/>
<LinkToContactPoints />
</div>
</Stack>
@ -123,4 +149,22 @@ const getStyles = (theme: GrafanaTheme2) => ({
gap: theme.spacing(1),
marginTop: theme.spacing(1),
}),
refreshButton: css({
color: theme.colors.text.secondary,
cursor: 'pointer',
borderRadius: theme.shape.radius.circle,
overflow: 'hidden',
}),
loading: css({
pointerEvents: 'none',
animation: 'rotation 2s infinite linear',
'@keyframes rotation': {
from: {
transform: 'rotate(720deg)',
},
to: {
transform: 'rotate(0deg)',
},
},
}),
});