Merge remote-tracking branch 'origin/main' into grafana-git-ui-sync

This commit is contained in:
Mariell Hoversholm committed 2025-02-13 09:20:15 +01:00
commit dbc4031a2a
54 files changed
+877 -216

No files matched your search

@@ -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<Props, State> {
};
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<ErrorBoundaryAlertProps> {
@@ -98,10 +107,10 @@ export class ErrorBoundaryAlert extends PureComponent<ErrorBoundaryAlertProps> {
};
render() {
const { title, children, style, dependencies } = this.props;
const { title, children, style, dependencies, errorLogger } = this.props;
return (
<ErrorBoundary dependencies={dependencies}>
<ErrorBoundary dependencies={dependencies} errorLogger={errorLogger}>
{({ error, errorInfo }) => {
if (!errorInfo) {
return children;
@@ -6,7 +6,7 @@ type SchemaVersionMigrationFunc func(map[string]interface{}) error
const (
MINIUM_VERSION = 36
LATEST_VERSION = 40
LATEST_VERSION = 41
)
var Migrations = map[int]SchemaVersionMigrationFunc{
@@ -14,6 +14,7 @@ var Migrations = map[int]SchemaVersionMigrationFunc{
38: V38,
39: V39,
40: V40,
41: V41,
}
func GetSchemaVersion(dash map[string]interface{}) int {
@@ -0,0 +1,11 @@
package schemaversion
func V41(dash map[string]interface{}) error {
dash["schemaVersion"] = int(41)
if timepicker, ok := dash["timepicker"].(map[string]interface{}); ok {
// time_options is a legacy property that was not used since grafana version 5
// therefore deprecating this property from the schema
delete(timepicker, "time_options")
}
return nil
}
@@ -0,0 +1,38 @@
package schemaversion_test
import (
"testing"
"github.com/grafana/grafana/pkg/apis/dashboard/migration/schemaversion"
)
func TestV41(t *testing.T) {
tests := []migrationTestCase{
{
name: "time_options is removed",
input: map[string]interface{}{
"title": "Test Dashboard",
"timepicker": map[string]interface{}{
"time_options": []string{"1m", "5m", "15m", "1h", "6h", "12h", "24h"},
},
},
expected: map[string]interface{}{
"title": "Test Dashboard",
"schemaVersion": 41,
"timepicker": map[string]interface{}{},
},
},
{
name: "timepicker is not set",
input: map[string]interface{}{
"title": "Test Dashboard",
},
expected: map[string]interface{}{
"title": "Test Dashboard",
"schemaVersion": 41,
},
},
}
runMigrationTests(t, tests, schemaversion.V41)
}
@@ -115,7 +115,9 @@
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timepicker": {
"time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
@@ -352,7 +352,9 @@
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timepicker": {
"time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
@@ -145,7 +145,9 @@
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timepicker": {
"time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
@@ -124,7 +124,9 @@
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timepicker": {
"time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
@@ -0,0 +1,136 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"links": [],
"panels": [
{
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.5.0-81438",
"targets": [
{
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"queryType": "randomWalk",
"refId": "A"
}
],
"title": "Panel Title",
"type": "timeseries"
}
],
"preload": false,
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
"weekStart": "",
"refresh": "",
"schemaVersion": 40
}
@@ -124,7 +124,19 @@
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timepicker": {
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
@@ -124,7 +124,19 @@
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timepicker": {
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
@@ -124,7 +124,19 @@
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timepicker": {
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
@@ -124,7 +124,19 @@
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timepicker": {
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
@@ -0,0 +1,132 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"links": [],
"panels": [
{
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {},
"title": "No Legend Config",
"type": "graph"
},
{
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"legend": {
"displayMode": "list",
"showLegend": true
}
},
"title": "Boolean Legend True"
},
{
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
},
"id": 3,
"options": {
"legend": {
"displayMode": "list",
"showLegend": false
}
},
"title": "Boolean Legend False"
},
{
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 8
},
"id": 4,
"options": {
"legend": {
"displayMode": "list",
"showLegend": false
}
},
"title": "Hidden DisplayMode"
},
{
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 16
},
"id": 5,
"options": {
"legend": {
"displayMode": "list",
"showLegend": false
}
},
"title": "ShowLegend False"
},
{
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 16
},
"id": 6,
"options": {
"legend": {
"displayMode": "table",
"showLegend": true
}
},
"title": "Visible Legend"
}
],
"preload": false,
"refresh": "",
"schemaVersion": 41,
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
"weekStart": ""
}
@@ -367,7 +367,19 @@
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timepicker": {
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
@@ -367,7 +367,19 @@
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timepicker": {
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
@@ -367,7 +367,19 @@
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timepicker": {
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
@@ -31,7 +31,10 @@
"mode": "palette-classic"
},
"custom": {
"displayMode": "basic"
"cellOptions": {
"mode": "basic",
"type": "gauge"
}
},
"mappings": [],
"thresholds": {
@@ -84,7 +87,10 @@
"mode": "palette-classic"
},
"custom": {
"displayMode": "gradient-gauge"
"cellOptions": {
"mode": "gradient",
"type": "gauge"
}
},
"mappings": [],
"thresholds": {
@@ -137,7 +143,10 @@
"mode": "palette-classic"
},
"custom": {
"displayMode": "lcd-gauge"
"cellOptions": {
"mode": "lcd",
"type": "gauge"
}
},
"mappings": [],
"thresholds": {
@@ -190,7 +199,10 @@
"mode": "palette-classic"
},
"custom": {
"displayMode": "color-background"
"cellOptions": {
"mode": "gradient",
"type": "color-background"
}
},
"mappings": [],
"thresholds": {
@@ -243,7 +255,10 @@
"mode": "palette-classic"
},
"custom": {
"displayMode": "color-background-solid"
"cellOptions": {
"mode": "basic",
"type": "color-background"
}
},
"mappings": [],
"thresholds": {
@@ -296,7 +311,9 @@
"mode": "palette-classic"
},
"custom": {
"displayMode": "some-other-mode"
"cellOptions": {
"type": "some-other-mode"
}
},
"mappings": [],
"thresholds": {
@@ -340,8 +357,8 @@
}
],
"preload": false,
"refresh": true,
"schemaVersion": 37,
"refresh": "",
"schemaVersion": 41,
"tags": [],
"templating": {
"list": []
@@ -147,7 +147,19 @@
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timepicker": {
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
@@ -147,7 +147,19 @@
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timepicker": {
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
@@ -124,9 +124,11 @@
{
"id": "timeSeriesTable",
"options": {
"refIdToStat": {
"A": "mean",
"B": "max"
"A": {
"stat": "mean"
},
"B": {
"stat": "max"
}
}
}
@@ -135,8 +137,8 @@
}
],
"preload": false,
"refresh": true,
"schemaVersion": 38,
"refresh": "",
"schemaVersion": 41,
"tags": [],
"templating": {
"list": []
@@ -126,7 +126,19 @@
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timepicker": {
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
@@ -116,8 +116,8 @@
}
],
"preload": false,
"refresh": true,
"schemaVersion": 39,
"refresh": "",
"schemaVersion": 41,
"tags": [],
"templating": {
"list": []
@@ -0,0 +1,134 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"links": [],
"panels": [
{
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "11.5.0-81438",
"targets": [
{
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"queryType": "randomWalk",
"refId": "A"
}
],
"title": "Panel Title",
"type": "timeseries"
}
],
"preload": false,
"refresh": "",
"schemaVersion": 41,
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "utc",
"title": "New dashboard",
"version": 0,
"weekStart": ""
}
@@ -37,11 +37,12 @@ func WrapHandler(handler http.Handler) func(req *http.Request) (*http.Response,
if err != nil {
return nil, err
}
defer cancel()
// The cancel happens in the goroutine we spawn, so as to not cancel it too early.
req = req.WithContext(ctx) // returns a shallow copy, so we can't do it as part of the adapter.
w := NewAdapter(req)
go func() {
defer cancel()
handler.ServeHTTP(w, req)
if err := w.CloseWriter(); err != nil {
klog.Errorf("error closing writer: %v", err)
+8 -5
View File
@@ -250,14 +250,17 @@ func (l *LibraryElementService) deleteLibraryElement(c context.Context, signedIn
return err
}
// then find the dashboards that were supposed to be connected to this element
_, requester := identity.WithServiceIdentity(c, signedInUser.GetOrgID())
dashs, err := l.dashboardsService.FindDashboards(c, &dashboards.FindPersistedDashboardsQuery{
// then find the dashboards that were supposed to be connected to this element.
// A identity may be able to delete a library element but not read all dashboards so we fetch then as the
// service user so we can prevent deletion of those connections
serviceCtx, serviceIdent := identity.WithServiceIdentity(c, signedInUser.GetOrgID())
dashs, err := l.dashboardsService.FindDashboards(serviceCtx, &dashboards.FindPersistedDashboardsQuery{
Type: searchstore.TypeDashboard,
OrgId: signedInUser.GetOrgID(),
OrgId: serviceIdent.GetOrgID(),
DashboardIds: dashboardIDs,
SignedInUser: requester, // a user may be able to delete a library element but not read all dashboards. We still need to run this check, so we don't allow deleting elements if dashboards are connected
SignedInUser: serviceIdent,
})
if err != nil {
return err
}
@@ -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 = () => (
<AlertmanagerPageWrapper navId="groups" accessType="instance">
<AlertGroups />
</AlertmanagerPageWrapper>
);
function AlertGroupsPage() {
return (
<AlertmanagerPageWrapper navId="groups" accessType="instance">
<AlertGroups />
</AlertmanagerPageWrapper>
);
}
export default AlertGroupsPage;
export default withPageErrorBoundary(AlertGroupsPage);
@@ -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
</Page>
);
}
export default withPageErrorBoundary(FeatureTogglePage);
@@ -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() {
</AlertmanagerPageWrapper>
);
}
export default withErrorBoundary(NewSilencePage, { style: 'page' });
export default withPageErrorBoundary(NewSilencePage);
@@ -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 = () => (
<AlertmanagerPageWrapper navId="am-routes" accessType="notification">
<NotificationPoliciesTabs />
</AlertmanagerPageWrapper>
);
function NotificationPoliciesPage() {
return (
<AlertmanagerPageWrapper navId="am-routes" accessType="notification">
<NotificationPoliciesTabs />
</AlertmanagerPageWrapper>
);
}
export default withErrorBoundary(NotificationPoliciesPage, { style: 'page' });
export default withPageErrorBoundary(NotificationPoliciesPage);
@@ -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);
@@ -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 <Suspense>{newView ? <RuleListV2 /> : <RuleListV1 />}</Suspense>;
};
export default RuleList;
export default withPageErrorBoundary(RuleList);
@@ -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 <Alert title={'Something went wrong loading the rule'}>{stringifyErrorLike(error)}</Alert>;
}
export default withErrorBoundary(RuleViewer, { style: 'page' });
export default withPageErrorBoundary(RuleViewer);
@@ -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 (
<SettingsProvider>
<SettingsContent />
@@ -47,3 +48,5 @@ function SettingsContent() {
</AlertingPageWrapper>
);
}
export default withPageErrorBoundary(SettingsPage);
@@ -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 => (
<AlertmanagerPageWrapper
navId="receivers"
accessType="notification"
pageNav={{
id: 'templates',
text: 'Notification templates',
subTitle: 'Create and edit a group of notification templates',
}}
>
<Routes>
<Route path=":name/edit" element={<EditMessageTemplate />} />
<Route path="new" element={<NewMessageTemplate />} />
<Route path=":name/duplicate" element={<DuplicateMessageTemplate />} />
</Routes>
</AlertmanagerPageWrapper>
);
function NotificationTemplates() {
return (
<AlertmanagerPageWrapper
navId="receivers"
accessType="notification"
pageNav={{
id: 'templates',
text: 'Notification templates',
subTitle: 'Create and edit a group of notification templates',
}}
>
<Routes>
<Route path=":name/edit" element={<EditMessageTemplate />} />
<Route path="new" element={<NewMessageTemplate />} />
<Route path=":name/duplicate" element={<DuplicateMessageTemplate />} />
</Routes>
</AlertmanagerPageWrapper>
);
}
export default withErrorBoundary(NotificationTemplates, { style: 'page' });
export default withPageErrorBoundary(NotificationTemplates);
@@ -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);
@@ -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 = <EntityNotFound entity="Notification template" />;
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 (
<AlertmanagerPageWrapper navId="receivers" accessType="notification">
<DuplicateMessageTemplateComponent />
</AlertmanagerPageWrapper>
);
}
export default withPageErrorBoundary(DuplicateMessageTemplate);
@@ -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);
@@ -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 = <EntityNotFound entity="Notification template" />;
const EditMessageTemplate = () => {
const EditMessageTemplateComponent = () => {
const { name } = useParams<{ name: string }>();
const templateUid = name ? decodeURIComponent(name) : undefined;
@@ -47,4 +49,12 @@ const EditMessageTemplate = () => {
return <TemplateForm alertmanager={selectedAlertmanager ?? ''} originalTemplate={currentData} />;
};
export default EditMessageTemplate;
function EditMessageTemplate() {
return (
<AlertmanagerPageWrapper navId="receivers" accessType="notification">
<EditMessageTemplateComponent />
</AlertmanagerPageWrapper>
);
}
export default withPageErrorBoundary(EditMessageTemplate);
@@ -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 <EntityNotFound entity="Alertmanager" />;
}
return (
<AlertmanagerPageWrapper navId="receivers" accessType="notification">
<TemplateForm alertmanager={selectedAlertmanager ?? ''} />
</AlertmanagerPageWrapper>
);
}
return <TemplateForm alertmanager={selectedAlertmanager} />;
};
export default NewMessageTemplate;
export default withPageErrorBoundary(NewMessageTemplate);
@@ -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 <GlobalConfigForm config={data} alertManagerSourceName={selectedAlertmanager!} />;
};
function NewMessageTemplatePage() {
function GlobalConfigPage() {
return (
<AlertmanagerPageWrapper navId="receivers" accessType="notification">
<NewMessageTemplate />
<GlobalConfig />
</AlertmanagerPageWrapper>
);
}
export default withErrorBoundary(NewMessageTemplatePage, { style: 'page' });
export default withPageErrorBoundary(GlobalConfigPage);
@@ -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 (
<ExportNewGrafanaRuleWrapper>
<ModifyExportRuleForm />
</ExportNewGrafanaRuleWrapper>
);
}
interface ExportNewGrafanaRuleWrapperProps {
children: React.ReactNode;
}
function ExportNewGrafanaRuleWrapper({ children }: ExportNewGrafanaRuleWrapperProps) {
function ExportNewGrafanaRulePage() {
return (
<AlertingPageWrapper
navId="alert-list"
@@ -24,7 +11,9 @@ function ExportNewGrafanaRuleWrapper({ children }: ExportNewGrafanaRuleWrapperPr
subTitle: 'Export a new rule definition in Terraform(HCL) format. Any changes you make will not be saved.',
}}
>
{children}
<ModifyExportRuleForm />
</AlertingPageWrapper>
);
}
export default withPageErrorBoundary(ExportNewGrafanaRulePage);
@@ -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<RuleIdentifier | undefined>(() => {
return ruleId.tryParse(id, true);
@@ -23,38 +23,13 @@ export default function GrafanaModifyExport() {
if (!ruleIdentifier) {
return (
<ModifyExportWrapper>
<Alert title="Invalid rule ID" severity="error">
The rule UID in the page URL is invalid. Please check the URL and try again.
</Alert>
</ModifyExportWrapper>
<Alert title="Invalid rule ID" severity="error">
The rule UID in the page URL is invalid. Please check the URL and try again.
</Alert>
);
}
return (
<ModifyExportWrapper>
<RuleModifyExport ruleIdentifier={ruleIdentifier} />
</ModifyExportWrapper>
);
}
interface ModifyExportWrapperProps {
children: React.ReactNode;
}
function ModifyExportWrapper({ children }: ModifyExportWrapperProps) {
return (
<AlertingPageWrapper
navId="alert-list"
pageNav={{
text: 'Modify export',
subTitle:
'Modify the current alert rule and export the rule definition in the format of your choice. Any changes you make will not be saved.',
}}
>
{children}
</AlertingPageWrapper>
);
return <RuleModifyExport ruleIdentifier={ruleIdentifier} />;
}
function RuleModifyExport({ ruleIdentifier }: { ruleIdentifier: RuleIdentifier }) {
@@ -105,3 +80,20 @@ function RuleModifyExport({ ruleIdentifier }: { ruleIdentifier: RuleIdentifier }
return <Alert title="Unknown error" />;
}
function GrafanaModifyExportPage() {
return (
<AlertingPageWrapper
navId="alert-list"
pageNav={{
text: 'Modify export',
subTitle:
'Modify the current alert rule and export the rule definition in the format of your choice. Any changes you make will not be saved.',
}}
>
<GrafanaModifyExport />
</AlertingPageWrapper>
);
}
export default withPageErrorBoundary(GrafanaModifyExportPage);
@@ -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 = () => (
<AlertmanagerPageWrapper
navId="am-routes"
pageNav={{
id: 'alert-policy-edit',
text: 'Edit mute timing',
}}
accessType="notification"
>
<EditTimingRoute />
</AlertmanagerPageWrapper>
);
function EditMuteTimingPage() {
return (
<AlertmanagerPageWrapper
navId="am-routes"
pageNav={{ id: 'alert-policy-edit', text: 'Edit mute timing' }}
accessType="notification"
>
<EditTimingRoute />
</AlertmanagerPageWrapper>
);
}
export default withErrorBoundary(EditMuteTimingPage, { style: 'page' });
export default withPageErrorBoundary(EditMuteTimingPage);
@@ -1,20 +1,18 @@
import { withErrorBoundary } from '@grafana/ui';
import { withPageErrorBoundary } from '../../withPageErrorBoundary';
import { AlertmanagerPageWrapper } from '../AlertingPageWrapper';
import MuteTimingForm from './MuteTimingForm';
const NewMuteTimingPage = () => (
<AlertmanagerPageWrapper
navId="am-routes"
pageNav={{
id: 'alert-policy-new',
text: 'Add mute timing',
}}
accessType="notification"
>
<MuteTimingForm />
</AlertmanagerPageWrapper>
);
function NewMuteTimingPage() {
return (
<AlertmanagerPageWrapper
navId="am-routes"
pageNav={{ id: 'alert-policy-new', text: 'Add mute timing' }}
accessType="notification"
>
<MuteTimingForm />
</AlertmanagerPageWrapper>
);
}
export default withErrorBoundary(NewMuteTimingPage, { style: 'page' });
export default withPageErrorBoundary(NewMuteTimingPage);
@@ -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);
@@ -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 (
<AlertingPageWrapper navId="alerts-history" isLoading={false}>
<CentralAlertHistoryScene />
</AlertingPageWrapper>
);
};
export default withErrorBoundary(HistoryPage, { style: 'page' });
}
export default withPageErrorBoundary(HistoryPage);
@@ -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() {
</AlertmanagerPageWrapper>
);
}
export default withErrorBoundary(ExistingSilenceEditorPage, { style: 'page' });
export default withPageErrorBoundary(ExistingSilenceEditorPage);
@@ -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() {
</AlertmanagerPageWrapper>
);
}
export default withErrorBoundary(SilencesTablePage, { style: 'page' });
export default withPageErrorBoundary(SilencesTablePage);
@@ -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() {
</AlertingPageWrapper>
);
}
export default withPageErrorBoundary(Home);
@@ -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<RuleEditorPathParams>();
@@ -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);
@@ -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
<AlertingPageWrapper navId="alert-list" isLoading={false} actions={null}>
<RulesFilter onClear={() => {}} />
{showListView ? <FilterView filterState={filterState} /> : <GroupedView />}
</AlertingPageWrapper>
);
},
{ style: 'page' }
);
return (
<>
<RulesFilter onClear={() => {}} />
{showListView ? <FilterView filterState={filterState} /> : <GroupedView />}
</>
);
}
export default RuleList;
export default function RuleListPage() {
return (
<AlertingPageWrapper navId="alert-list" isLoading={false} actions={null}>
<RuleList />
</AlertingPageWrapper>
);
}
@@ -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<P extends {} = {}>(
Component: ComponentType<P>,
errorBoundaryProps: Omit<ErrorBoundaryAlertProps, 'children' | 'errorLogger' | 'style'> = {}
): ComponentType<P> {
return withErrorBoundary(Component, {
...errorBoundaryProps,
style: 'page',
errorLogger: logError,
});
}