diff --git a/public/app/features/alerting/unified/MenuItemPauseRule.tsx b/public/app/features/alerting/unified/MenuItemPauseRule.tsx
new file mode 100644
index 00000000000..a1d83103768
--- /dev/null
+++ b/public/app/features/alerting/unified/MenuItemPauseRule.tsx
@@ -0,0 +1,78 @@
+import React from 'react';
+
+import { Menu } from '@grafana/ui';
+import { alertRuleApi } from 'app/features/alerting/unified/api/alertRuleApi';
+import { isGrafanaRulerRule, isGrafanaRulerRulePaused } from 'app/features/alerting/unified/utils/rules';
+import { CombinedRule } from 'app/types/unified-alerting';
+
+const { useUpdateRuleMutation } = alertRuleApi;
+
+interface Props {
+ rule: CombinedRule;
+ /**
+ * Method invoked after the request to change the paused state has completed
+ */
+ onPauseChange?: () => {};
+}
+
+/**
+ * Menu item to display correct text for pausing/resuming an alert,
+ * and triggering API call to do so
+ */
+const MenuItemPauseRule = ({ rule, onPauseChange }: Props) => {
+ const { group } = rule;
+ const [updateRule] = useUpdateRuleMutation();
+ const isPaused = isGrafanaRulerRule(rule.rulerRule) && isGrafanaRulerRulePaused(rule.rulerRule);
+ const icon = isPaused ? 'play' : 'pause';
+ const title = isPaused ? 'Resume alert evaluation' : 'Pause alert evaluation';
+
+ /**
+ * Triggers API call to update the current rule to the new `is_paused` state
+ */
+ const setRulePause = async (newIsPaused: boolean) => {
+ if (!isGrafanaRulerRule(rule.rulerRule)) {
+ return;
+ }
+ const ruleUid = rule.rulerRule.grafana_alert.uid;
+
+ // Parse the rules into correct format for API
+ const modifiedRules = group.rules.map((groupRule) => {
+ if (isGrafanaRulerRule(groupRule.rulerRule) && groupRule.rulerRule.grafana_alert.uid === ruleUid) {
+ return {
+ ...groupRule.rulerRule,
+ grafana_alert: {
+ ...groupRule.rulerRule.grafana_alert,
+ is_paused: newIsPaused,
+ },
+ };
+ }
+
+ return groupRule.rulerRule!;
+ });
+
+ const payload = {
+ interval: group.interval!,
+ name: group.name,
+ rules: modifiedRules,
+ };
+
+ await updateRule({
+ nameSpaceUID: rule.namespace.uid || rule.rulerRule.grafana_alert.namespace_uid,
+ payload,
+ }).unwrap();
+
+ onPauseChange?.();
+ };
+
+ return (
+
{
+ setRulePause(!isPaused);
+ }}
+ />
+ );
+};
+
+export default MenuItemPauseRule;
diff --git a/public/app/features/alerting/unified/components/rules/RuleActionsButtons.tsx b/public/app/features/alerting/unified/components/rules/RuleActionsButtons.tsx
index 1c42aa0bd18..ee7601b2a47 100644
--- a/public/app/features/alerting/unified/components/rules/RuleActionsButtons.tsx
+++ b/public/app/features/alerting/unified/components/rules/RuleActionsButtons.tsx
@@ -17,7 +17,7 @@ import {
Stack,
} from '@grafana/ui';
import { useAppNotification } from 'app/core/copy/appNotification';
-import { alertRuleApi } from 'app/features/alerting/unified/api/alertRuleApi';
+import MenuItemPauseRule from 'app/features/alerting/unified/MenuItemPauseRule';
import { INSTANCES_DISPLAY_LIMIT } from 'app/features/alerting/unified/components/rules/RuleDetails';
import { useRulesFilter } from 'app/features/alerting/unified/hooks/useFilteredRules';
import { useDispatch } from 'app/types';
@@ -28,13 +28,11 @@ import { deleteRuleAction, fetchAllPromAndRulerRulesAction } from '../../state/a
import { getRulesSourceName } from '../../utils/datasource';
import { createShareLink, createViewLink } from '../../utils/misc';
import * as ruleId from '../../utils/rule-id';
-import { isGrafanaRulerRule, isGrafanaRulerRulePaused } from '../../utils/rules';
+import { isGrafanaRulerRule } from '../../utils/rules';
import { createUrl } from '../../utils/url';
import { RedirectToCloneRule } from './CloneRule';
-const { useUpdateRuleMutation } = alertRuleApi;
-
export const matchesWidth = (width: number) => window.matchMedia(`(max-width: ${width}px)`).matches;
interface Props {
@@ -47,7 +45,6 @@ export const RuleActionsButtons = ({ rule, rulesSource }: Props) => {
const location = useLocation();
const notifyApp = useAppNotification();
const style = useStyles2(getStyles);
- const [updateRule] = useUpdateRuleMutation();
const [redirectToClone, setRedirectToClone] = useState<
{ identifier: RuleIdentifier; isProvisioned: boolean } | undefined
@@ -75,47 +72,6 @@ export const RuleActionsButtons = ({ rule, rulesSource }: Props) => {
const buttons: JSX.Element[] = [];
const moreActions: JSX.Element[] = [];
- /**
- * Triggers API call to update the current rule to the new `is_paused` state
- */
- const setRulePause = async (newIsPaused: boolean) => {
- if (!isGrafanaRulerRule(rule.rulerRule)) {
- return;
- }
- const ruleUid = rule.rulerRule.grafana_alert.uid;
-
- // Parse the rules into correct format for API
- const modifiedRules = group.rules.map((groupRule) => {
- if (isGrafanaRulerRule(groupRule.rulerRule) && groupRule.rulerRule.grafana_alert.uid === ruleUid) {
- return {
- ...groupRule.rulerRule,
- grafana_alert: {
- ...groupRule.rulerRule.grafana_alert,
- is_paused: newIsPaused,
- },
- };
- }
-
- return groupRule.rulerRule!;
- });
-
- const payload = {
- interval: group.interval!,
- name: group.name,
- rules: modifiedRules,
- };
-
- await updateRule({ nameSpaceUID: rule.namespace.uid!, payload }).unwrap();
-
- // Uses INSTANCES_DISPLAY_LIMIT + 1 here as exporting LIMIT_ALERTS from RuleList has the side effect
- // of breaking some unrelated tests in Policy.test.tsx due to mocking approach
- const limitAlerts = hasActiveFilters ? undefined : INSTANCES_DISPLAY_LIMIT + 1;
- // Trigger a re-fetch of the rules table
- // TODO: Migrate rules table functionality to RTK Query, so we instead rely
- // on tag invalidation (or optimistic cache updates) for this
- await dispatch(fetchAllPromAndRulerRulesAction(false, { limitAlerts }));
- };
-
const deleteRule = () => {
if (ruleToDelete && ruleToDelete.rulerRule) {
const identifier = ruleId.fromRulerRule(
@@ -172,15 +128,17 @@ export const RuleActionsButtons = ({ rule, rulesSource }: Props) => {
);
- const isPaused = isGrafanaRulerRule(rule.rulerRule) && isGrafanaRulerRulePaused(rule.rulerRule);
- const icon = isPaused ? 'play' : 'pause';
- const title = isPaused ? 'Resume alert evaluation' : 'Pause alert evaluation';
moreActions.push(
- {
- setRulePause(!isPaused);
+ {
+ // Uses INSTANCES_DISPLAY_LIMIT + 1 here as exporting LIMIT_ALERTS from RuleList has the side effect
+ // of breaking some unrelated tests in Policy.test.tsx due to mocking approach
+ const limitAlerts = hasActiveFilters ? undefined : INSTANCES_DISPLAY_LIMIT + 1;
+ // Trigger a re-fetch of the rules table
+ // TODO: Migrate rules table functionality to RTK Query, so we instead rely
+ // on tag invalidation (or optimistic cache updates) for this
+ await dispatch(fetchAllPromAndRulerRulesAction(false, { limitAlerts }));
}}
/>
);