From fbf96916aa23a83346c3955c38914032300b9a29 Mon Sep 17 00:00:00 2001 From: Konrad Lalik Date: Thu, 13 Feb 2025 09:20:45 +0100 Subject: [PATCH] Alerting: Use alerting-specific error boundary for page components (#99980) Use alerting-specific error boundary for page components --- .../ErrorBoundary/ErrorBoundary.tsx | 15 ++++-- .../features/alerting/unified/AlertGroups.tsx | 15 +++--- .../alerting/unified/AlertingNotEnabled.tsx | 6 ++- .../alerting/unified/NewSilencePage.tsx | 5 +- .../unified/NotificationPoliciesPage.tsx | 17 +++--- .../alerting/unified/RedirectToRuleViewer.tsx | 5 +- .../features/alerting/unified/RuleList.tsx | 3 +- .../features/alerting/unified/RuleViewer.tsx | 5 +- .../features/alerting/unified/Settings.tsx | 5 +- .../features/alerting/unified/Templates.tsx | 41 +++++++------- .../contact-points/ContactPoints.tsx | 4 +- .../DuplicateMessageTemplate.tsx | 14 ++++- .../contact-points/EditContactPoint.tsx | 5 +- .../contact-points/EditMessageTemplate.tsx | 14 ++++- .../contact-points/NewMessageTemplate.tsx | 20 +++---- .../components/GlobalConfig.tsx | 11 ++-- .../export/ExportNewGrafanaRule.tsx | 21 ++------ .../components/export/GrafanaModifyExport.tsx | 54 ++++++++----------- .../mute-timings/EditMuteTiming.tsx | 27 +++++----- .../components/mute-timings/NewMuteTiming.tsx | 28 +++++----- .../components/receivers/NewReceiverView.tsx | 4 +- .../CentralAlertHistoryPage.tsx | 10 ++-- .../components/silences/SilencesEditor.tsx | 5 +- .../components/silences/SilencesTable.tsx | 5 +- .../features/alerting/unified/home/Home.tsx | 5 +- .../unified/rule-editor/RuleEditor.tsx | 6 ++- .../unified/rule-list/RuleList.v1.tsx | 4 +- .../unified/rule-list/RuleList.v2.tsx | 39 +++++++------- .../unified/withPageErrorBoundary.tsx | 25 +++++++++ 29 files changed, 238 insertions(+), 180 deletions(-) create mode 100644 public/app/features/alerting/unified/withPageErrorBoundary.tsx diff --git a/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.tsx b/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.tsx index 312d6e76029..8c50da47478 100644 --- a/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.tsx +++ b/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.tsx @@ -21,6 +21,8 @@ interface Props { onError?: (error: Error) => void; /** Callback error state is cleared due to recover props change */ onRecover?: () => void; + /** Default error logger - Faro by default */ + errorLogger?: (error: Error) => void; } interface State { @@ -35,7 +37,12 @@ export class ErrorBoundary extends PureComponent { }; componentDidCatch(error: Error, errorInfo: ErrorInfo) { - faro?.api?.pushError(error); + const logger = this.props.errorLogger ?? faro?.api?.pushError; + + if (logger) { + logger(error); + } + this.setState({ error, errorInfo }); if (this.props.onError) { @@ -89,6 +96,8 @@ export interface ErrorBoundaryAlertProps { /** Will re-render children after error if recover values changes */ dependencies?: unknown[]; + /** Default error logger - Faro by default */ + errorLogger?: (error: Error) => void; } export class ErrorBoundaryAlert extends PureComponent { @@ -98,10 +107,10 @@ export class ErrorBoundaryAlert extends PureComponent { }; render() { - const { title, children, style, dependencies } = this.props; + const { title, children, style, dependencies, errorLogger } = this.props; return ( - + {({ error, errorInfo }) => { if (!errorInfo) { return children; diff --git a/public/app/features/alerting/unified/AlertGroups.tsx b/public/app/features/alerting/unified/AlertGroups.tsx index 3422a093990..2d5863f7aea 100644 --- a/public/app/features/alerting/unified/AlertGroups.tsx +++ b/public/app/features/alerting/unified/AlertGroups.tsx @@ -19,6 +19,7 @@ import { NOTIFICATIONS_POLL_INTERVAL_MS } from './utils/constants'; import { GRAFANA_RULES_SOURCE_NAME } from './utils/datasource'; import { getFiltersFromUrlParams } from './utils/misc'; import { initialAsyncRequestState } from './utils/redux'; +import { withPageErrorBoundary } from './withPageErrorBoundary'; const AlertGroups = () => { const { selectedAlertmanager } = useAlertmanager(); @@ -89,10 +90,12 @@ const AlertGroups = () => { ); }; -const AlertGroupsPage = () => ( - - - -); +function AlertGroupsPage() { + return ( + + + + ); +} -export default AlertGroupsPage; +export default withPageErrorBoundary(AlertGroupsPage); diff --git a/public/app/features/alerting/unified/AlertingNotEnabled.tsx b/public/app/features/alerting/unified/AlertingNotEnabled.tsx index ca8a10aa487..9250fafed2c 100644 --- a/public/app/features/alerting/unified/AlertingNotEnabled.tsx +++ b/public/app/features/alerting/unified/AlertingNotEnabled.tsx @@ -1,7 +1,9 @@ import { NavModel } from '@grafana/data'; import { Page } from 'app/core/components/Page/Page'; -export default function FeatureTogglePage() { +import { withPageErrorBoundary } from './withPageErrorBoundary'; + +function FeatureTogglePage() { const navModel: NavModel = { node: { text: 'Alerting is not enabled', @@ -25,3 +27,5 @@ enabled = true ); } + +export default withPageErrorBoundary(FeatureTogglePage); diff --git a/public/app/features/alerting/unified/NewSilencePage.tsx b/public/app/features/alerting/unified/NewSilencePage.tsx index 859b9c3875e..2f2dc4e79cd 100644 --- a/public/app/features/alerting/unified/NewSilencePage.tsx +++ b/public/app/features/alerting/unified/NewSilencePage.tsx @@ -1,6 +1,5 @@ import { useLocation } from 'react-router-dom-v5-compat'; -import { withErrorBoundary } from '@grafana/ui'; import { defaultsFromQuery, getDefaultSilenceFormValues, @@ -12,6 +11,7 @@ import { AlertmanagerPageWrapper } from './components/AlertingPageWrapper'; import { GrafanaAlertmanagerDeliveryWarning } from './components/GrafanaAlertmanagerDeliveryWarning'; import { SilencesEditor } from './components/silences/SilencesEditor'; import { useAlertmanager } from './state/AlertmanagerContext'; +import { withPageErrorBoundary } from './withPageErrorBoundary'; const SilencesEditorComponent = () => { const location = useLocation(); @@ -48,4 +48,5 @@ function NewSilencePage() { ); } -export default withErrorBoundary(NewSilencePage, { style: 'page' }); + +export default withPageErrorBoundary(NewSilencePage); diff --git a/public/app/features/alerting/unified/NotificationPoliciesPage.tsx b/public/app/features/alerting/unified/NotificationPoliciesPage.tsx index 6b4d1b9b5e7..25d2ef8cbf8 100644 --- a/public/app/features/alerting/unified/NotificationPoliciesPage.tsx +++ b/public/app/features/alerting/unified/NotificationPoliciesPage.tsx @@ -2,7 +2,7 @@ import { css } from '@emotion/css'; import { useState } from 'react'; import { GrafanaTheme2, UrlQueryMap } from '@grafana/data'; -import { Tab, TabContent, TabsBar, useStyles2, withErrorBoundary } from '@grafana/ui'; +import { Tab, TabContent, TabsBar, useStyles2 } from '@grafana/ui'; import { useQueryParams } from 'app/core/hooks/useQueryParams'; import { useMuteTimings } from 'app/features/alerting/unified/components/mute-timings/useMuteTimings'; import { NotificationPoliciesList } from 'app/features/alerting/unified/components/notification-policies/NotificationPoliciesList'; @@ -12,6 +12,7 @@ import { AlertmanagerPageWrapper } from './components/AlertingPageWrapper'; import { GrafanaAlertmanagerDeliveryWarning } from './components/GrafanaAlertmanagerDeliveryWarning'; import { MuteTimingsTable } from './components/mute-timings/MuteTimingsTable'; import { useAlertmanager } from './state/AlertmanagerContext'; +import { withPageErrorBoundary } from './withPageErrorBoundary'; enum ActiveTab { NotificationPolicies = 'notification_policies', @@ -104,10 +105,12 @@ function getActiveTabFromUrl(queryParams: UrlQueryMap, defaultTab: ActiveTab): Q }; } -const NotificationPoliciesPage = () => ( - - - -); +function NotificationPoliciesPage() { + return ( + + + + ); +} -export default withErrorBoundary(NotificationPoliciesPage, { style: 'page' }); +export default withPageErrorBoundary(NotificationPoliciesPage); diff --git a/public/app/features/alerting/unified/RedirectToRuleViewer.tsx b/public/app/features/alerting/unified/RedirectToRuleViewer.tsx index e5375ad9133..bb032b5a9fd 100644 --- a/public/app/features/alerting/unified/RedirectToRuleViewer.tsx +++ b/public/app/features/alerting/unified/RedirectToRuleViewer.tsx @@ -5,7 +5,7 @@ import { useLocation } from 'react-use'; import { GrafanaTheme2 } from '@grafana/data'; import { config, isFetchError } from '@grafana/runtime'; -import { Alert, Card, Icon, LoadingPlaceholder, useStyles2, withErrorBoundary } from '@grafana/ui'; +import { Alert, Card, Icon, LoadingPlaceholder, useStyles2 } from '@grafana/ui'; import { AlertLabels } from './components/AlertLabels'; import { RuleViewerLayout } from './components/rule-viewer/RuleViewerLayout'; @@ -13,6 +13,7 @@ import { useCloudCombinedRulesMatching } from './hooks/useCombinedRule'; import { getRulesSourceByName } from './utils/datasource'; import { createViewLink } from './utils/misc'; import { unescapePathSeparators } from './utils/rule-id'; +import { withPageErrorBoundary } from './withPageErrorBoundary'; const pageTitle = 'Find rule'; const subUrl = config.appSubUrl; @@ -153,4 +154,4 @@ function getStyles(theme: GrafanaTheme2) { }; } -export default withErrorBoundary(RedirectToRuleViewer, { style: 'page' }); +export default withPageErrorBoundary(RedirectToRuleViewer); diff --git a/public/app/features/alerting/unified/RuleList.tsx b/public/app/features/alerting/unified/RuleList.tsx index 9c38d4bf049..f51f440bd14 100644 --- a/public/app/features/alerting/unified/RuleList.tsx +++ b/public/app/features/alerting/unified/RuleList.tsx @@ -3,6 +3,7 @@ import { Suspense, lazy } from 'react'; import { config } from '@grafana/runtime'; import RuleListV1 from './rule-list/RuleList.v1'; +import { withPageErrorBoundary } from './withPageErrorBoundary'; const RuleListV2 = lazy(() => import('./rule-list/RuleList.v2')); const RuleList = () => { @@ -11,4 +12,4 @@ const RuleList = () => { return {newView ? : }; }; -export default RuleList; +export default withPageErrorBoundary(RuleList); diff --git a/public/app/features/alerting/unified/RuleViewer.tsx b/public/app/features/alerting/unified/RuleViewer.tsx index 9584539e41b..9df84a41b18 100644 --- a/public/app/features/alerting/unified/RuleViewer.tsx +++ b/public/app/features/alerting/unified/RuleViewer.tsx @@ -3,7 +3,7 @@ import { useParams } from 'react-router-dom-v5-compat'; import { NavModelItem } from '@grafana/data'; import { isFetchError } from '@grafana/runtime'; -import { Alert, withErrorBoundary } from '@grafana/ui'; +import { Alert } from '@grafana/ui'; import { EntityNotFound } from 'app/core/components/PageNotFound/EntityNotFound'; import { AlertingPageWrapper } from './components/AlertingPageWrapper'; @@ -12,6 +12,7 @@ import DetailView, { ActiveTab, useActiveTab } from './components/rule-viewer/Ru import { useCombinedRule } from './hooks/useCombinedRule'; import { stringifyErrorLike } from './utils/misc'; import { getRuleIdFromPathname, parse as parseRuleId } from './utils/rule-id'; +import { withPageErrorBoundary } from './withPageErrorBoundary'; const RuleViewer = (): JSX.Element => { const params = useParams(); @@ -86,4 +87,4 @@ function ErrorMessage({ error }: ErrorMessageProps) { return {stringifyErrorLike(error)}; } -export default withErrorBoundary(RuleViewer, { style: 'page' }); +export default withPageErrorBoundary(RuleViewer); diff --git a/public/app/features/alerting/unified/Settings.tsx b/public/app/features/alerting/unified/Settings.tsx index 099bde572b0..7d3a91dafdf 100644 --- a/public/app/features/alerting/unified/Settings.tsx +++ b/public/app/features/alerting/unified/Settings.tsx @@ -6,8 +6,9 @@ import { useEditConfigurationDrawer } from './components/settings/ConfigurationD import { ExternalAlertmanagers } from './components/settings/ExternalAlertmanagers'; import InternalAlertmanager from './components/settings/InternalAlertmanager'; import { SettingsProvider, useSettings } from './components/settings/SettingsContext'; +import { withPageErrorBoundary } from './withPageErrorBoundary'; -export default function SettingsPage() { +function SettingsPage() { return ( @@ -47,3 +48,5 @@ function SettingsContent() { ); } + +export default withPageErrorBoundary(SettingsPage); diff --git a/public/app/features/alerting/unified/Templates.tsx b/public/app/features/alerting/unified/Templates.tsx index d32ea0541b4..f3b8b011f36 100644 --- a/public/app/features/alerting/unified/Templates.tsx +++ b/public/app/features/alerting/unified/Templates.tsx @@ -1,28 +1,29 @@ import { Route, Routes } from 'react-router-dom-v5-compat'; -import { withErrorBoundary } from '@grafana/ui'; - import { AlertmanagerPageWrapper } from './components/AlertingPageWrapper'; import DuplicateMessageTemplate from './components/contact-points/DuplicateMessageTemplate'; import EditMessageTemplate from './components/contact-points/EditMessageTemplate'; import NewMessageTemplate from './components/contact-points/NewMessageTemplate'; +import { withPageErrorBoundary } from './withPageErrorBoundary'; -const NotificationTemplates = (): JSX.Element => ( - - - } /> - } /> - } /> - - -); +function NotificationTemplates() { + return ( + + + } /> + } /> + } /> + + + ); +} -export default withErrorBoundary(NotificationTemplates, { style: 'page' }); +export default withPageErrorBoundary(NotificationTemplates); diff --git a/public/app/features/alerting/unified/components/contact-points/ContactPoints.tsx b/public/app/features/alerting/unified/components/contact-points/ContactPoints.tsx index 5ea42c97584..0d2f6cf447e 100644 --- a/public/app/features/alerting/unified/components/contact-points/ContactPoints.tsx +++ b/public/app/features/alerting/unified/components/contact-points/ContactPoints.tsx @@ -12,7 +12,6 @@ import { TabContent, TabsBar, Text, - withErrorBoundary, } from '@grafana/ui'; import { contextSrv } from 'app/core/core'; import { Trans, t } from 'app/core/internationalization'; @@ -25,6 +24,7 @@ import { usePagination } from '../../hooks/usePagination'; import { useURLSearchParams } from '../../hooks/useURLSearchParams'; import { useAlertmanager } from '../../state/AlertmanagerContext'; import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource'; +import { withPageErrorBoundary } from '../../withPageErrorBoundary'; import { AlertmanagerPageWrapper } from '../AlertingPageWrapper'; import { GrafanaAlertmanagerDeliveryWarning } from '../GrafanaAlertmanagerDeliveryWarning'; @@ -270,4 +270,4 @@ function ContactPointsPage() { ); } -export default withErrorBoundary(ContactPointsPage, { style: 'page' }); +export default withPageErrorBoundary(ContactPointsPage); diff --git a/public/app/features/alerting/unified/components/contact-points/DuplicateMessageTemplate.tsx b/public/app/features/alerting/unified/components/contact-points/DuplicateMessageTemplate.tsx index 3ded1052b5d..bc89effea98 100644 --- a/public/app/features/alerting/unified/components/contact-points/DuplicateMessageTemplate.tsx +++ b/public/app/features/alerting/unified/components/contact-points/DuplicateMessageTemplate.tsx @@ -8,13 +8,15 @@ import { useAlertmanager } from '../../state/AlertmanagerContext'; import { generateCopiedName } from '../../utils/duplicate'; import { stringifyErrorLike } from '../../utils/misc'; import { updateDefinesWithUniqueValue } from '../../utils/templates'; +import { withPageErrorBoundary } from '../../withPageErrorBoundary'; +import { AlertmanagerPageWrapper } from '../AlertingPageWrapper'; import { TemplateForm } from '../receivers/TemplateForm'; import { useGetNotificationTemplate, useNotificationTemplates } from './useNotificationTemplates'; const notFoundComponent = ; -const DuplicateMessageTemplate = () => { +const DuplicateMessageTemplateComponent = () => { const { selectedAlertmanager } = useAlertmanager(); const { name } = useParams<{ name: string }>(); const templateUid = name ? decodeURIComponent(name) : undefined; @@ -63,4 +65,12 @@ const DuplicateMessageTemplate = () => { ); }; -export default DuplicateMessageTemplate; +function DuplicateMessageTemplate() { + return ( + + + + ); +} + +export default withPageErrorBoundary(DuplicateMessageTemplate); diff --git a/public/app/features/alerting/unified/components/contact-points/EditContactPoint.tsx b/public/app/features/alerting/unified/components/contact-points/EditContactPoint.tsx index 96a00fc1d33..5e9deb4b3da 100644 --- a/public/app/features/alerting/unified/components/contact-points/EditContactPoint.tsx +++ b/public/app/features/alerting/unified/components/contact-points/EditContactPoint.tsx @@ -1,10 +1,11 @@ import { useParams } from 'react-router-dom-v5-compat'; -import { Alert, LoadingPlaceholder, withErrorBoundary } from '@grafana/ui'; +import { Alert, LoadingPlaceholder } from '@grafana/ui'; import { useGetContactPoint } from 'app/features/alerting/unified/components/contact-points/useContactPoints'; import { stringifyErrorLike } from 'app/features/alerting/unified/utils/misc'; import { useAlertmanager } from '../../state/AlertmanagerContext'; +import { withPageErrorBoundary } from '../../withPageErrorBoundary'; import { AlertmanagerPageWrapper } from '../AlertingPageWrapper'; import { EditReceiverView } from '../receivers/EditReceiverView'; @@ -50,4 +51,4 @@ function EditContactPointPage() { ); } -export default withErrorBoundary(EditContactPointPage, { style: 'page' }); +export default withPageErrorBoundary(EditContactPointPage); diff --git a/public/app/features/alerting/unified/components/contact-points/EditMessageTemplate.tsx b/public/app/features/alerting/unified/components/contact-points/EditMessageTemplate.tsx index 6c7df2a7fad..926e1c5404b 100644 --- a/public/app/features/alerting/unified/components/contact-points/EditMessageTemplate.tsx +++ b/public/app/features/alerting/unified/components/contact-points/EditMessageTemplate.tsx @@ -6,13 +6,15 @@ import { EntityNotFound } from 'app/core/components/PageNotFound/EntityNotFound' import { isNotFoundError } from '../../api/util'; import { useAlertmanager } from '../../state/AlertmanagerContext'; import { stringifyErrorLike } from '../../utils/misc'; +import { withPageErrorBoundary } from '../../withPageErrorBoundary'; +import { AlertmanagerPageWrapper } from '../AlertingPageWrapper'; import { TemplateForm } from '../receivers/TemplateForm'; import { useGetNotificationTemplate } from './useNotificationTemplates'; const notFoundComponent = ; -const EditMessageTemplate = () => { +const EditMessageTemplateComponent = () => { const { name } = useParams<{ name: string }>(); const templateUid = name ? decodeURIComponent(name) : undefined; @@ -47,4 +49,12 @@ const EditMessageTemplate = () => { return ; }; -export default EditMessageTemplate; +function EditMessageTemplate() { + return ( + + + + ); +} + +export default withPageErrorBoundary(EditMessageTemplate); diff --git a/public/app/features/alerting/unified/components/contact-points/NewMessageTemplate.tsx b/public/app/features/alerting/unified/components/contact-points/NewMessageTemplate.tsx index e11ac531390..7cdd2286be1 100644 --- a/public/app/features/alerting/unified/components/contact-points/NewMessageTemplate.tsx +++ b/public/app/features/alerting/unified/components/contact-points/NewMessageTemplate.tsx @@ -1,16 +1,16 @@ -import { EntityNotFound } from 'app/core/components/PageNotFound/EntityNotFound'; - import { useAlertmanager } from '../../state/AlertmanagerContext'; +import { withPageErrorBoundary } from '../../withPageErrorBoundary'; +import { AlertmanagerPageWrapper } from '../AlertingPageWrapper'; import { TemplateForm } from '../receivers/TemplateForm'; -const NewMessageTemplate = () => { +function NewMessageTemplate() { const { selectedAlertmanager } = useAlertmanager(); - if (!selectedAlertmanager) { - return ; - } + return ( + + + + ); +} - return ; -}; - -export default NewMessageTemplate; +export default withPageErrorBoundary(NewMessageTemplate); diff --git a/public/app/features/alerting/unified/components/contact-points/components/GlobalConfig.tsx b/public/app/features/alerting/unified/components/contact-points/components/GlobalConfig.tsx index e5514b01d66..42ec581c529 100644 --- a/public/app/features/alerting/unified/components/contact-points/components/GlobalConfig.tsx +++ b/public/app/features/alerting/unified/components/contact-points/components/GlobalConfig.tsx @@ -1,11 +1,12 @@ -import { Alert, withErrorBoundary } from '@grafana/ui'; +import { Alert } from '@grafana/ui'; import { useAlertmanagerConfig } from '../../../hooks/useAlertmanagerConfig'; import { useAlertmanager } from '../../../state/AlertmanagerContext'; +import { withPageErrorBoundary } from '../../../withPageErrorBoundary'; import { AlertmanagerPageWrapper } from '../../AlertingPageWrapper'; import { GlobalConfigForm } from '../../receivers/GlobalConfigForm'; -const NewMessageTemplate = () => { +const GlobalConfig = () => { const { selectedAlertmanager } = useAlertmanager(); const { data, isLoading, error } = useAlertmanagerConfig(selectedAlertmanager); @@ -28,12 +29,12 @@ const NewMessageTemplate = () => { return ; }; -function NewMessageTemplatePage() { +function GlobalConfigPage() { return ( - + ); } -export default withErrorBoundary(NewMessageTemplatePage, { style: 'page' }); +export default withPageErrorBoundary(GlobalConfigPage); diff --git a/public/app/features/alerting/unified/components/export/ExportNewGrafanaRule.tsx b/public/app/features/alerting/unified/components/export/ExportNewGrafanaRule.tsx index 2e99bc0dc41..614f74eaa2a 100644 --- a/public/app/features/alerting/unified/components/export/ExportNewGrafanaRule.tsx +++ b/public/app/features/alerting/unified/components/export/ExportNewGrafanaRule.tsx @@ -1,21 +1,8 @@ -import * as React from 'react'; - +import { withPageErrorBoundary } from '../../withPageErrorBoundary'; import { AlertingPageWrapper } from '../AlertingPageWrapper'; import { ModifyExportRuleForm } from '../rule-editor/alert-rule-form/ModifyExportRuleForm'; -export default function ExportNewGrafanaRule() { - return ( - - - - ); -} - -interface ExportNewGrafanaRuleWrapperProps { - children: React.ReactNode; -} - -function ExportNewGrafanaRuleWrapper({ children }: ExportNewGrafanaRuleWrapperProps) { +function ExportNewGrafanaRulePage() { return ( - {children} + ); } + +export default withPageErrorBoundary(ExportNewGrafanaRulePage); diff --git a/public/app/features/alerting/unified/components/export/GrafanaModifyExport.tsx b/public/app/features/alerting/unified/components/export/GrafanaModifyExport.tsx index 5aca1d77824..1c4241fe519 100644 --- a/public/app/features/alerting/unified/components/export/GrafanaModifyExport.tsx +++ b/public/app/features/alerting/unified/components/export/GrafanaModifyExport.tsx @@ -1,4 +1,3 @@ -import * as React from 'react'; import { useMemo } from 'react'; import { useParams } from 'react-router-dom-v5-compat'; @@ -12,10 +11,11 @@ import { stringifyErrorLike } from '../../utils/misc'; import * as ruleId from '../../utils/rule-id'; import { isGrafanaRulerRule } from '../../utils/rules'; import { createRelativeUrl } from '../../utils/url'; +import { withPageErrorBoundary } from '../../withPageErrorBoundary'; import { AlertingPageWrapper } from '../AlertingPageWrapper'; import { ModifyExportRuleForm } from '../rule-editor/alert-rule-form/ModifyExportRuleForm'; -export default function GrafanaModifyExport() { +function GrafanaModifyExport() { const { id } = useParams(); const ruleIdentifier = useMemo(() => { return ruleId.tryParse(id, true); @@ -23,38 +23,13 @@ export default function GrafanaModifyExport() { if (!ruleIdentifier) { return ( - - - The rule UID in the page URL is invalid. Please check the URL and try again. - - + + The rule UID in the page URL is invalid. Please check the URL and try again. + ); } - return ( - - - - ); -} - -interface ModifyExportWrapperProps { - children: React.ReactNode; -} - -function ModifyExportWrapper({ children }: ModifyExportWrapperProps) { - return ( - - {children} - - ); + return ; } function RuleModifyExport({ ruleIdentifier }: { ruleIdentifier: RuleIdentifier }) { @@ -105,3 +80,20 @@ function RuleModifyExport({ ruleIdentifier }: { ruleIdentifier: RuleIdentifier } return ; } + +function GrafanaModifyExportPage() { + return ( + + + + ); +} + +export default withPageErrorBoundary(GrafanaModifyExportPage); diff --git a/public/app/features/alerting/unified/components/mute-timings/EditMuteTiming.tsx b/public/app/features/alerting/unified/components/mute-timings/EditMuteTiming.tsx index f73b1a1d1af..f697b935408 100644 --- a/public/app/features/alerting/unified/components/mute-timings/EditMuteTiming.tsx +++ b/public/app/features/alerting/unified/components/mute-timings/EditMuteTiming.tsx @@ -1,10 +1,10 @@ import { Navigate } from 'react-router-dom-v5-compat'; -import { withErrorBoundary } from '@grafana/ui'; import { useGetMuteTiming } from 'app/features/alerting/unified/components/mute-timings/useMuteTimings'; import { useURLSearchParams } from 'app/features/alerting/unified/hooks/useURLSearchParams'; import { useAlertmanager } from '../../state/AlertmanagerContext'; +import { withPageErrorBoundary } from '../../withPageErrorBoundary'; import { AlertmanagerPageWrapper } from '../AlertingPageWrapper'; import MuteTimingForm from './MuteTimingForm'; @@ -38,17 +38,16 @@ const EditTimingRoute = () => { ); }; -const EditMuteTimingPage = () => ( - - - -); +function EditMuteTimingPage() { + return ( + + + + ); +} -export default withErrorBoundary(EditMuteTimingPage, { style: 'page' }); +export default withPageErrorBoundary(EditMuteTimingPage); diff --git a/public/app/features/alerting/unified/components/mute-timings/NewMuteTiming.tsx b/public/app/features/alerting/unified/components/mute-timings/NewMuteTiming.tsx index b73dda0e94f..c60e6d0f990 100644 --- a/public/app/features/alerting/unified/components/mute-timings/NewMuteTiming.tsx +++ b/public/app/features/alerting/unified/components/mute-timings/NewMuteTiming.tsx @@ -1,20 +1,18 @@ -import { withErrorBoundary } from '@grafana/ui'; - +import { withPageErrorBoundary } from '../../withPageErrorBoundary'; import { AlertmanagerPageWrapper } from '../AlertingPageWrapper'; import MuteTimingForm from './MuteTimingForm'; -const NewMuteTimingPage = () => ( - - - -); +function NewMuteTimingPage() { + return ( + + + + ); +} -export default withErrorBoundary(NewMuteTimingPage, { style: 'page' }); +export default withPageErrorBoundary(NewMuteTimingPage); diff --git a/public/app/features/alerting/unified/components/receivers/NewReceiverView.tsx b/public/app/features/alerting/unified/components/receivers/NewReceiverView.tsx index 4f2a3322da4..9258dddfc89 100644 --- a/public/app/features/alerting/unified/components/receivers/NewReceiverView.tsx +++ b/public/app/features/alerting/unified/components/receivers/NewReceiverView.tsx @@ -1,7 +1,7 @@ -import { withErrorBoundary } from '@grafana/ui'; import { useAlertmanager } from 'app/features/alerting/unified/state/AlertmanagerContext'; import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource'; +import { withPageErrorBoundary } from '../../withPageErrorBoundary'; import { AlertmanagerPageWrapper } from '../AlertingPageWrapper'; import { CloudReceiverForm } from './form/CloudReceiverForm'; @@ -24,4 +24,4 @@ function NewReceiverViewPage() { ); } -export default withErrorBoundary(NewReceiverViewPage, { style: 'page' }); +export default withPageErrorBoundary(NewReceiverViewPage); diff --git a/public/app/features/alerting/unified/components/rules/central-state-history/CentralAlertHistoryPage.tsx b/public/app/features/alerting/unified/components/rules/central-state-history/CentralAlertHistoryPage.tsx index eb767a1df2f..91463309afd 100644 --- a/public/app/features/alerting/unified/components/rules/central-state-history/CentralAlertHistoryPage.tsx +++ b/public/app/features/alerting/unified/components/rules/central-state-history/CentralAlertHistoryPage.tsx @@ -1,14 +1,14 @@ -import { withErrorBoundary } from '@grafana/ui'; - +import { withPageErrorBoundary } from '../../../withPageErrorBoundary'; import { AlertingPageWrapper } from '../../AlertingPageWrapper'; import { CentralAlertHistoryScene } from './CentralAlertHistoryScene'; -const HistoryPage = () => { +function HistoryPage() { return ( ); -}; -export default withErrorBoundary(HistoryPage, { style: 'page' }); +} + +export default withPageErrorBoundary(HistoryPage); diff --git a/public/app/features/alerting/unified/components/silences/SilencesEditor.tsx b/public/app/features/alerting/unified/components/silences/SilencesEditor.tsx index c72cb1e5521..2a1c1be335c 100644 --- a/public/app/features/alerting/unified/components/silences/SilencesEditor.tsx +++ b/public/app/features/alerting/unified/components/silences/SilencesEditor.tsx @@ -25,7 +25,6 @@ import { Stack, TextArea, useStyles2, - withErrorBoundary, } from '@grafana/ui'; import { Trans } from 'app/core/internationalization'; import { SilenceCreatedResponse, alertSilencesApi } from 'app/features/alerting/unified/api/alertSilencesApi'; @@ -38,6 +37,7 @@ import { useAlertmanager } from '../../state/AlertmanagerContext'; import { SilenceFormFields } from '../../types/silence-form'; import { matcherFieldToMatcher } from '../../utils/alertmanager'; import { makeAMLink } from '../../utils/misc'; +import { withPageErrorBoundary } from '../../withPageErrorBoundary'; import { AlertmanagerPageWrapper } from '../AlertingPageWrapper'; import { GrafanaAlertmanagerDeliveryWarning } from '../GrafanaAlertmanagerDeliveryWarning'; @@ -296,4 +296,5 @@ function ExistingSilenceEditorPage() { ); } -export default withErrorBoundary(ExistingSilenceEditorPage, { style: 'page' }); + +export default withPageErrorBoundary(ExistingSilenceEditorPage); diff --git a/public/app/features/alerting/unified/components/silences/SilencesTable.tsx b/public/app/features/alerting/unified/components/silences/SilencesTable.tsx index 6eaf819d11a..2faa5a2b1c8 100644 --- a/public/app/features/alerting/unified/components/silences/SilencesTable.tsx +++ b/public/app/features/alerting/unified/components/silences/SilencesTable.tsx @@ -12,7 +12,6 @@ import { LoadingPlaceholder, Stack, useStyles2, - withErrorBoundary, } from '@grafana/ui'; import { useQueryParams } from 'app/core/hooks/useQueryParams'; import { Trans } from 'app/core/internationalization'; @@ -27,6 +26,7 @@ import { AlertmanagerAction, useAlertmanagerAbility } from '../../hooks/useAbili import { useAlertmanager } from '../../state/AlertmanagerContext'; import { parsePromQLStyleMatcherLooseSafe } from '../../utils/matchers'; import { getSilenceFiltersFromUrlParams, makeAMLink, stringifyErrorLike } from '../../utils/misc'; +import { withPageErrorBoundary } from '../../withPageErrorBoundary'; import { AlertmanagerPageWrapper } from '../AlertingPageWrapper'; import { Authorize } from '../Authorize'; import { DynamicTable, DynamicTableColumnProps, DynamicTableItemProps } from '../DynamicTable'; @@ -393,4 +393,5 @@ function SilencesTablePage() { ); } -export default withErrorBoundary(SilencesTablePage, { style: 'page' }); + +export default withPageErrorBoundary(SilencesTablePage); diff --git a/public/app/features/alerting/unified/home/Home.tsx b/public/app/features/alerting/unified/home/Home.tsx index 898bfdebefb..023b6a237a3 100644 --- a/public/app/features/alerting/unified/home/Home.tsx +++ b/public/app/features/alerting/unified/home/Home.tsx @@ -5,12 +5,13 @@ import { Box, Stack, Tab, TabContent, TabsBar } from '@grafana/ui'; import { AlertingPageWrapper } from '../components/AlertingPageWrapper'; import { isLocalDevEnv } from '../utils/misc'; +import { withPageErrorBoundary } from '../withPageErrorBoundary'; import GettingStarted, { WelcomeHeader } from './GettingStarted'; import { getInsightsScenes, insightsIsAvailable } from './Insights'; import { PluginIntegrations } from './PluginIntegrations'; -export default function Home() { +function Home() { const insightsEnabled = (insightsIsAvailable() || isLocalDevEnv()) && Boolean(config.featureToggles.alertingInsights); const [activeTab, setActiveTab] = useState<'insights' | 'overview'>(insightsEnabled ? 'insights' : 'overview'); @@ -51,3 +52,5 @@ export default function Home() { ); } + +export default withPageErrorBoundary(Home); diff --git a/public/app/features/alerting/unified/rule-editor/RuleEditor.tsx b/public/app/features/alerting/unified/rule-editor/RuleEditor.tsx index b5adba57cf2..42585600052 100644 --- a/public/app/features/alerting/unified/rule-editor/RuleEditor.tsx +++ b/public/app/features/alerting/unified/rule-editor/RuleEditor.tsx @@ -2,7 +2,6 @@ import { useCallback } from 'react'; import { useParams } from 'react-router-dom-v5-compat'; import { NavModelItem } from '@grafana/data'; -import { withErrorBoundary } from '@grafana/ui'; import { RuleIdentifier } from 'app/types/unified-alerting'; import { AlertWarning } from '../AlertWarning'; @@ -11,6 +10,7 @@ import { AlertRuleForm } from '../components/rule-editor/alert-rule-form/AlertRu import { useURLSearchParams } from '../hooks/useURLSearchParams'; import { useRulesAccess } from '../utils/accessControlHooks'; import * as ruleId from '../utils/rule-id'; +import { withPageErrorBoundary } from '../withPageErrorBoundary'; import { CloneRuleEditor } from './CloneRuleEditor'; import { ExistingRuleEditor } from './ExistingRuleEditor'; @@ -78,7 +78,9 @@ const RuleEditor = () => { ); }; -export default withErrorBoundary(RuleEditor, { style: 'page' }); +// The pageNav property makes it difficult to only rely on AlertingPageWrapper +// to catch errors. +export default withPageErrorBoundary(RuleEditor); function useRuleEditorPathParams() { const params = useParams(); diff --git a/public/app/features/alerting/unified/rule-list/RuleList.v1.tsx b/public/app/features/alerting/unified/rule-list/RuleList.v1.tsx index d90c4b238c0..759e7b48474 100644 --- a/public/app/features/alerting/unified/rule-list/RuleList.v1.tsx +++ b/public/app/features/alerting/unified/rule-list/RuleList.v1.tsx @@ -4,7 +4,7 @@ import { useAsyncFn, useInterval } from 'react-use'; import { urlUtil } from '@grafana/data'; import { logInfo } from '@grafana/runtime'; -import { Button, LinkButton, Stack, withErrorBoundary } from '@grafana/ui'; +import { Button, LinkButton, Stack } from '@grafana/ui'; import { useQueryParams } from 'app/core/hooks/useQueryParams'; import { Trans } from 'app/core/internationalization'; import { useDispatch } from 'app/types'; @@ -155,7 +155,7 @@ const RuleListV1 = () => { ); }; -export default withErrorBoundary(RuleListV1, { style: 'page' }); +export default RuleListV1; export function CreateAlertButton() { const [createRuleSupported, createRuleAllowed] = useAlertingAbility(AlertingAction.CreateAlertRule); diff --git a/public/app/features/alerting/unified/rule-list/RuleList.v2.tsx b/public/app/features/alerting/unified/rule-list/RuleList.v2.tsx index b8ccbb2fa8f..86949386e3a 100644 --- a/public/app/features/alerting/unified/rule-list/RuleList.v2.tsx +++ b/public/app/features/alerting/unified/rule-list/RuleList.v2.tsx @@ -1,5 +1,3 @@ -import { withErrorBoundary } from '@grafana/ui'; - import { AlertingPageWrapper } from '../components/AlertingPageWrapper'; import RulesFilter from '../components/rules/Filter/RulesFilter'; import { SupportedView } from '../components/rules/Filter/RulesViewModeSelector'; @@ -9,24 +7,25 @@ import { useURLSearchParams } from '../hooks/useURLSearchParams'; import { FilterView } from './FilterView'; import { GroupedView } from './GroupedView'; -const RuleList = withErrorBoundary( - () => { - const [queryParams] = useURLSearchParams(); - const { filterState, hasActiveFilters } = useRulesFilter(); +function RuleList() { + const [queryParams] = useURLSearchParams(); + const { filterState, hasActiveFilters } = useRulesFilter(); - const view: SupportedView = queryParams.get('view') === 'list' ? 'list' : 'grouped'; - const showListView = hasActiveFilters || view === 'list'; + const view: SupportedView = queryParams.get('view') === 'list' ? 'list' : 'grouped'; + const showListView = hasActiveFilters || view === 'list'; - return ( - // We don't want to show the Loading... indicator for the whole page. - // We show separate indicators for Grafana-managed and Cloud rules - - {}} /> - {showListView ? : } - - ); - }, - { style: 'page' } -); + return ( + <> + {}} /> + {showListView ? : } + + ); +} -export default RuleList; +export default function RuleListPage() { + return ( + + + + ); +} diff --git a/public/app/features/alerting/unified/withPageErrorBoundary.tsx b/public/app/features/alerting/unified/withPageErrorBoundary.tsx new file mode 100644 index 00000000000..e53d5bb2e0d --- /dev/null +++ b/public/app/features/alerting/unified/withPageErrorBoundary.tsx @@ -0,0 +1,25 @@ +import { ComponentType } from 'react'; + +import { ErrorBoundaryAlertProps, withErrorBoundary } from '@grafana/ui'; + +import { logError } from './Analytics'; + +/** + * HOC for wrapping alerting page in an error boundary. + * It provides alerting-specific error handling. + * + * @param Component - the react component to wrap in error boundary + * @param errorBoundaryProps - error boundary options + * + * @public + */ +export function withPageErrorBoundary

( + Component: ComponentType

, + errorBoundaryProps: Omit = {} +): ComponentType

{ + return withErrorBoundary(Component, { + ...errorBoundaryProps, + style: 'page', + errorLogger: logError, + }); +}