diff --git a/src/platform/packages/shared/response-ops/rule_form/src/edit_rule_form.tsx b/src/platform/packages/shared/response-ops/rule_form/src/edit_rule_form.tsx
index 5e53930d40e05..7b6c69a031c99 100644
--- a/src/platform/packages/shared/response-ops/rule_form/src/edit_rule_form.tsx
+++ b/src/platform/packages/shared/response-ops/rule_form/src/edit_rule_form.tsx
@@ -26,7 +26,7 @@ import {
} from './rule_form_errors';
import { RULE_EDIT_ERROR_TEXT, RULE_EDIT_SUCCESS_TEXT } from './translations';
import { getAvailableRuleTypes, parseRuleCircuitBreakerErrorMessage } from './utils';
-import { DEFAULT_VALID_CONSUMERS, getDefaultFormData } from './constants';
+import { DEFAULT_VALID_CONSUMERS, RuleFormStepId, getDefaultFormData } from './constants';
export interface EditRuleFormProps {
id: string;
@@ -38,6 +38,7 @@ export interface EditRuleFormProps {
onSubmit?: (ruleId: string) => void;
onChangeMetaData?: (metadata?: RuleTypeMetaData) => void;
initialMetadata?: RuleTypeMetaData;
+ initialEditStep?: RuleFormStepId;
}
export const EditRuleForm = (props: EditRuleFormProps) => {
@@ -51,6 +52,7 @@ export const EditRuleForm = (props: EditRuleFormProps) => {
isFlyout,
onChangeMetaData,
initialMetadata,
+ initialEditStep,
} = props;
const { http, notifications, docLinks, ruleTypeRegistry, application, fieldsMetadata, ...deps } =
plugins;
@@ -229,6 +231,7 @@ export const EditRuleForm = (props: EditRuleFormProps) => {
onSave={onSave}
onCancel={onCancel}
onChangeMetaData={onChangeMetaData}
+ initialEditStep={initialEditStep}
/>
);
diff --git a/src/platform/packages/shared/response-ops/rule_form/src/rule_flyout/rule_flyout.test.tsx b/src/platform/packages/shared/response-ops/rule_form/src/rule_flyout/rule_flyout.test.tsx
index 2dee8c39d0910..9967ad6005d9c 100644
--- a/src/platform/packages/shared/response-ops/rule_form/src/rule_flyout/rule_flyout.test.tsx
+++ b/src/platform/packages/shared/response-ops/rule_form/src/rule_flyout/rule_flyout.test.tsx
@@ -16,6 +16,7 @@ import {
RULE_FORM_PAGE_RULE_DETAILS_TITLE_SHORT,
} from '../translations';
import { RuleFormData } from '../types';
+import { RuleFormStepId } from '../constants';
jest.mock('../rule_definition', () => ({
RuleDefinition: () =>
,
@@ -118,6 +119,44 @@ describe('ruleFlyout', () => {
expect(await screen.findByTestId('ruleFlyoutFooterNextStepButton')).toBeInTheDocument();
});
+ test('omitting `initialStep` causes default behavior with step 1 selected', () => {
+ const { getByText } = render();
+
+ expect(getByText('Current step is 1'));
+ expect(getByText('Step 2 is incomplete'));
+ expect(getByText('Step 3 is incomplete'));
+ });
+
+ test('setting `initialStep` to `RuleFormStepId.DEFINITION` will make step 1 the current step', () => {
+ const { getByText } = render(
+
+ );
+
+ expect(getByText('Current step is 1'));
+ expect(getByText('Step 2 is incomplete'));
+ expect(getByText('Step 3 is incomplete'));
+ });
+
+ test('setting `initialStep` to `RuleFormStepId.ACTION` will make step 1 the current step', () => {
+ const { getByText } = render(
+
+ );
+
+ expect(getByText('Step 1 is complete'));
+ expect(getByText('Current step is 2'));
+ expect(getByText('Step 3 is incomplete'));
+ });
+
+ test('setting `initialStep` to `RuleFormStepId.DETAILS` will make step 1 the current step', () => {
+ const { getByText } = render(
+
+ );
+
+ expect(getByText('Step 1 is complete'));
+ expect(getByText('Step 2 is incomplete'));
+ expect(getByText('Current step is 3'));
+ });
+
test('should call onSave when save button is pressed', async () => {
render();
diff --git a/src/platform/packages/shared/response-ops/rule_form/src/rule_flyout/rule_flyout.tsx b/src/platform/packages/shared/response-ops/rule_form/src/rule_flyout/rule_flyout.tsx
index 64407f60f1c7b..ead6556bb29e8 100644
--- a/src/platform/packages/shared/response-ops/rule_form/src/rule_flyout/rule_flyout.tsx
+++ b/src/platform/packages/shared/response-ops/rule_form/src/rule_flyout/rule_flyout.tsx
@@ -23,6 +23,7 @@ interface RuleFlyoutProps {
onCancel?: () => void;
onSave: (formData: RuleFormData) => void;
onChangeMetaData?: (metadata?: RuleTypeMetaData) => void;
+ initialEditStep?: RuleFormStepId;
}
// This component is only responsible for the CONTENT of the EuiFlyout. See `flyout/rule_form_flyout.tsx` for the
@@ -38,8 +39,9 @@ export const RuleFlyout = ({
// we're displaying the confirmation modal for closing the flyout.
onCancel: onClose = () => {},
onChangeMetaData = () => {},
+ initialEditStep,
}: RuleFlyoutProps) => {
- const [initialStep, setInitialStep] = useState(undefined);
+ const [initialStep, setInitialStep] = useState(initialEditStep);
const [isConfirmCloseModalVisible, setIsConfirmCloseModalVisible] = useState(false);
const {
diff --git a/src/platform/packages/shared/response-ops/rule_form/src/rule_form.tsx b/src/platform/packages/shared/response-ops/rule_form/src/rule_form.tsx
index 6b107da591283..e5f99415cd1e0 100644
--- a/src/platform/packages/shared/response-ops/rule_form/src/rule_form.tsx
+++ b/src/platform/packages/shared/response-ops/rule_form/src/rule_form.tsx
@@ -19,6 +19,7 @@ import {
RULE_FORM_ROUTE_PARAMS_ERROR_TITLE,
} from './translations';
import { RuleFormData, RuleFormPlugins, RuleTypeMetaData } from './types';
+import { RuleFormStepId } from './constants';
const queryClient = new QueryClient();
@@ -41,6 +42,7 @@ export interface RuleFormProps>;
initialMetadata?: MetaData;
+ initialEditStep?: RuleFormStepId;
}
export const RuleForm = (
@@ -65,6 +67,7 @@ export const RuleForm = (
showMustacheAutocompleteSwitch,
initialValues,
initialMetadata,
+ initialEditStep,
} = props;
const {
@@ -122,6 +125,7 @@ export const RuleForm = (
showMustacheAutocompleteSwitch={showMustacheAutocompleteSwitch}
connectorFeatureId={connectorFeatureId}
initialMetadata={initialMetadata}
+ initialEditStep={initialEditStep}
/>
);
}
@@ -175,25 +179,26 @@ export const RuleForm = (
docLinks,
ruleTypeRegistry,
actionTypeRegistry,
+ fieldsMetadata,
contentManagement,
+ onChangeMetaData,
id,
ruleTypeId,
- validConsumers,
- multiConsumerSelection,
onCancel,
onSubmit,
- onChangeMetaData,
isFlyout,
showMustacheAutocompleteSwitch,
connectorFeatureId,
initialMetadata,
+ initialEditStep,
consumer,
+ multiConsumerSelection,
hideInterval,
+ validConsumers,
filteredRuleTypes,
shouldUseRuleProducer,
canShowConsumerSelection,
initialValues,
- fieldsMetadata,
]);
return (
diff --git a/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/alert_details.tsx b/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/alert_details.tsx
index 09481ab1f36e4..a369d0a0ad81b 100644
--- a/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/alert_details.tsx
+++ b/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/alert_details.tsx
@@ -18,8 +18,8 @@ import {
EuiTabbedContentTab,
useEuiTheme,
EuiFlexGroup,
- EuiMarkdownFormat,
EuiNotificationBadge,
+ EuiIcon,
} from '@elastic/eui';
import {
AlertStatus,
@@ -39,6 +39,7 @@ import { usePageReady } from '@kbn/ebt-tools';
import { RelatedAlerts } from './components/related_alerts/related_alerts';
import { AlertDetailsSource } from './types';
import { SourceBar } from './components';
+import { InvestigationGuide } from './components/investigation_guide';
import { StatusBar } from './components/status_bar';
import { observabilityFeatureId } from '../../../common';
import { useKibana } from '../../utils/kibana_react';
@@ -119,7 +120,7 @@ export function AlertDetails() {
const userCasesPermissions = canUseCases([observabilityFeatureId]);
const ruleId = alertDetail?.formatted.fields[ALERT_RULE_UUID];
const { rule, refetch } = useFetchRule({
- ruleId,
+ ruleId: ruleId || '',
});
const onSuccessAddSuggestedDashboard = useCallback(async () => {
@@ -323,24 +324,26 @@ export function AlertDetails() {
{
id: 'investigation_guide',
name: (
-
+ <>
+
+ {rule?.artifacts?.investigation_guide?.blob && (
+
+
+
+ )}
+ >
),
'data-test-subj': 'investigationGuideTab',
- disabled: !rule?.artifacts?.investigation_guide?.blob,
content: (
- <>
-
-
- {rule?.artifacts?.investigation_guide?.blob ?? ''}
-
- >
+
),
},
{
@@ -402,6 +405,8 @@ export function AlertDetails() {
alertStatus={alertStatus}
onUntrackAlert={onUntrackAlert}
onUpdate={onUpdate}
+ rule={rule}
+ refetch={refetch}
/>
,
],
diff --git a/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/AlertDetailsRuleFormFlyout.tsx b/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/AlertDetailsRuleFormFlyout.tsx
new file mode 100644
index 0000000000000..62b740f2a221e
--- /dev/null
+++ b/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/AlertDetailsRuleFormFlyout.tsx
@@ -0,0 +1,55 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { RuleFormFlyout } from '@kbn/response-ops-rule-form/flyout';
+import { RuleFormStepId } from '@kbn/response-ops-rule-form/src/constants';
+import type { Rule } from '@kbn/triggers-actions-ui-plugin/public';
+import { useKibana } from '../../../utils/kibana_react';
+
+export interface AlertDetailsRuleFormFlyoutBaseProps {
+ onUpdate?: () => void;
+ refetch: () => void;
+ rule?: Rule;
+}
+
+interface Props extends AlertDetailsRuleFormFlyoutBaseProps {
+ initialEditStep?: RuleFormStepId;
+ isRuleFormFlyoutOpen: boolean;
+ setIsRuleFormFlyoutOpen: React.Dispatch;
+ rule: Rule;
+}
+
+export function AlertDetailsRuleFormFlyout({
+ initialEditStep,
+ onUpdate,
+ refetch,
+ isRuleFormFlyoutOpen,
+ setIsRuleFormFlyoutOpen,
+ rule,
+}: Props) {
+ const { services } = useKibana();
+ if (!isRuleFormFlyoutOpen) return null;
+ const {
+ triggersActionsUi: { ruleTypeRegistry, actionTypeRegistry },
+ } = services;
+ return (
+ {
+ setIsRuleFormFlyoutOpen(false);
+ }}
+ onSubmit={() => {
+ onUpdate?.();
+ refetch();
+ setIsRuleFormFlyoutOpen(false);
+ }}
+ initialEditStep={initialEditStep}
+ />
+ );
+}
diff --git a/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/header_actions.test.tsx b/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/header_actions.test.tsx
index 4b61258dbd058..33739c8e90cc6 100644
--- a/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/header_actions.test.tsx
+++ b/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/header_actions.test.tsx
@@ -104,6 +104,12 @@ describe('Header Actions', () => {
alertIndex={'alert-index'}
alertStatus={alertWithGroupsAndTags.fields[ALERT_STATUS] as AlertStatus}
onUntrackAlert={mockOnUntrackAlert}
+ refetch={jest.fn()}
+ // @ts-expect-error partial implementation for testing
+ rule={{
+ id: mockRuleId,
+ name: mockRuleName,
+ }}
/>
);
@@ -129,6 +135,12 @@ describe('Header Actions', () => {
alertIndex={'alert-index'}
alertStatus={alertWithGroupsAndTags.fields[ALERT_STATUS] as AlertStatus}
onUntrackAlert={mockOnUntrackAlert}
+ refetch={jest.fn()}
+ // @ts-expect-error partial implementation for testing
+ rule={{
+ id: mockRuleId,
+ name: mockRuleName,
+ }}
/>
);
@@ -141,6 +153,7 @@ describe('Header Actions', () => {
alert={alertWithGroupsAndTags}
alertStatus={alertWithGroupsAndTags.fields[ALERT_STATUS] as AlertStatus}
onUntrackAlert={mockOnUntrackAlert}
+ refetch={jest.fn()}
/>
);
expect(queryByTestId('alert-details-header-actions-menu-button')).toBeTruthy();
@@ -153,6 +166,12 @@ describe('Header Actions', () => {
alert={alertWithGroupsAndTags}
alertStatus={alertWithGroupsAndTags.fields[ALERT_STATUS] as AlertStatus}
onUntrackAlert={mockOnUntrackAlert}
+ refetch={jest.fn()}
+ // @ts-expect-error partial implementation for testing
+ rule={{
+ id: mockRuleId,
+ name: mockRuleName,
+ }}
/>
);
@@ -166,6 +185,12 @@ describe('Header Actions', () => {
alert={alertWithGroupsAndTags}
alertStatus={alertWithGroupsAndTags.fields[ALERT_STATUS] as AlertStatus}
onUntrackAlert={mockOnUntrackAlert}
+ refetch={jest.fn()}
+ // @ts-expect-error partial implementation for testing
+ rule={{
+ id: mockRuleId,
+ name: mockRuleName,
+ }}
/>
);
@@ -180,6 +205,12 @@ describe('Header Actions', () => {
alert={alertWithGroupsAndTags}
alertStatus={alertWithGroupsAndTags.fields[ALERT_STATUS] as AlertStatus}
onUntrackAlert={mockOnUntrackAlert}
+ refetch={jest.fn()}
+ // @ts-expect-error partial implementation for testing
+ rule={{
+ id: mockRuleId,
+ name: mockRuleName,
+ }}
/>
);
@@ -193,6 +224,12 @@ describe('Header Actions', () => {
alert={alertWithGroupsAndTags}
alertStatus={alertWithGroupsAndTags.fields[ALERT_STATUS] as AlertStatus}
onUntrackAlert={mockOnUntrackAlert}
+ refetch={jest.fn()}
+ // @ts-expect-error partial implementation for testing
+ rule={{
+ id: mockRuleId,
+ name: mockRuleName,
+ }}
/>
);
@@ -218,6 +255,7 @@ describe('Header Actions', () => {
alert={alertWithGroupsAndTags}
alertStatus={alertWithGroupsAndTags.fields[ALERT_STATUS] as AlertStatus}
onUntrackAlert={mockOnUntrackAlert}
+ refetch={jest.fn()}
/>
);
@@ -231,6 +269,7 @@ describe('Header Actions', () => {
alert={untrackedAlert}
alertStatus={untrackedAlert.fields[ALERT_STATUS] as AlertStatus}
onUntrackAlert={mockOnUntrackAlert}
+ refetch={jest.fn()}
/>
);
@@ -244,6 +283,7 @@ describe('Header Actions', () => {
alert={alertWithGroupsAndTags}
alertStatus={alertWithGroupsAndTags.fields[ALERT_STATUS] as AlertStatus}
onUntrackAlert={mockOnUntrackAlert}
+ refetch={jest.fn()}
/>
);
fireEvent.click(await findByTestId('alert-details-header-actions-menu-button'));
diff --git a/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/header_actions.tsx b/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/header_actions.tsx
index e2883c30d14b6..569248e447845 100644
--- a/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/header_actions.tsx
+++ b/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/header_actions.tsx
@@ -8,7 +8,6 @@
import React, { useCallback, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { noop } from 'lodash';
-import { RuleFormFlyout } from '@kbn/response-ops-rule-form/flyout';
import { CaseAttachmentsWithoutOwner } from '@kbn/cases-plugin/public/types';
import { AttachmentType } from '@kbn/cases-plugin/common';
import {
@@ -29,17 +28,19 @@ import {
} from '@kbn/rule-data-utils';
import { useKibana } from '../../../utils/kibana_react';
-import { useFetchRule } from '../../../hooks/use_fetch_rule';
import type { TopAlert } from '../../../typings/alerts';
import { paths } from '../../../../common/locators/paths';
import { useBulkUntrackAlerts } from '../hooks/use_bulk_untrack_alerts';
+import {
+ AlertDetailsRuleFormFlyout,
+ type AlertDetailsRuleFormFlyoutBaseProps,
+} from './AlertDetailsRuleFormFlyout';
-export interface HeaderActionsProps {
+export interface HeaderActionsProps extends AlertDetailsRuleFormFlyoutBaseProps {
alert: TopAlert | null;
alertIndex?: string;
alertStatus?: AlertStatus;
onUntrackAlert: () => void;
- onUpdate?: () => void;
}
export function HeaderActions({
@@ -48,26 +49,19 @@ export function HeaderActions({
alertStatus,
onUntrackAlert,
onUpdate,
+ rule,
+ refetch,
}: HeaderActionsProps) {
const { services } = useKibana();
const {
cases: {
hooks: { useCasesAddToExistingCaseModal },
},
- triggersActionsUi: {
- ruleTypeRegistry,
- actionTypeRegistry,
- getRuleSnoozeModal: RuleSnoozeModal,
- },
+ triggersActionsUi: { getRuleSnoozeModal: RuleSnoozeModal },
http,
} = services;
- const { rule, refetch } = useFetchRule({
- ruleId: alert?.fields[ALERT_RULE_UUID] || '',
- });
-
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
- const [ruleConditionsFlyoutOpen, setRuleConditionsFlyoutOpen] = useState(false);
const [snoozeModalOpen, setSnoozeModalOpen] = useState(false);
const selectCaseModal = useCasesAddToExistingCaseModal();
@@ -84,6 +78,8 @@ export function HeaderActions({
}
}, [alert, untrackAlerts, onUntrackAlert]);
+ const [alertDetailsRuleFormFlyoutOpen, setAlertDetailsRuleFormFlyoutOpen] = useState(false);
+
const handleTogglePopover = () => setIsPopoverOpen(!isPopoverOpen);
const handleClosePopover = () => setIsPopoverOpen(false);
@@ -107,11 +103,6 @@ export function HeaderActions({
selectCaseModal.open({ getAttachments: () => attachments });
};
- const handleEditRuleDetails = () => {
- setIsPopoverOpen(false);
- setRuleConditionsFlyoutOpen(true);
- };
-
const handleOpenSnoozeModal = () => {
setIsPopoverOpen(false);
setSnoozeModalOpen(true);
@@ -175,7 +166,10 @@ export function HeaderActions({
size="s"
color="text"
iconType="pencil"
- onClick={handleEditRuleDetails}
+ onClick={() => {
+ setIsPopoverOpen(false);
+ setAlertDetailsRuleFormFlyoutOpen(true);
+ }}
disabled={!alert?.fields[ALERT_RULE_UUID] || !rule}
data-test-subj="edit-rule-button"
>
@@ -225,20 +219,15 @@ export function HeaderActions({
- {rule && ruleConditionsFlyoutOpen ? (
- {
- setRuleConditionsFlyoutOpen(false);
- }}
- onSubmit={() => {
- setRuleConditionsFlyoutOpen(false);
- onUpdate?.();
- refetch();
- }}
+ {rule && (
+
- ) : null}
+ )}
{rule && snoozeModalOpen ? (
{
+ return {
+ // we mock the response-ops flyout because we aren't testing it here
+ RuleFormFlyout: () => Mock Flyout
,
+ };
+});
+
+describe('InvestigationGuide', () => {
+ beforeEach(() => {
+ jest.spyOn(kibana, 'useKibana').mockReturnValue({
+ services: {
+ triggersActionsUi: {
+ // @ts-expect-error partial implementation for mocking
+ ruleTypeRegistry: {
+ get: jest.fn(),
+ },
+ // @ts-expect-error partial implementation for mocking
+ actionTypeRegistry: {
+ get: jest.fn(),
+ },
+ },
+ },
+ });
+ jest.clearAllMocks();
+ });
+
+ it('provides an empty state that will open the rule form flyout', async () => {
+ const mockRule = { id: 'mock' };
+ const { getByRole, getByText } = render(
+ {}}
+ refetch={() => {}}
+ // @ts-expect-error internal hook call is mocked, do not need real values
+ rule={mockRule}
+ />
+ );
+
+ // grab add guide button for functionality testing
+ const addGuideButton = getByRole('button', { name: 'Add guide' });
+ expect(addGuideButton).toBeInTheDocument();
+
+ // verify that clicking the add guide button opens the flyout
+ await act(() => fireEvent.click(addGuideButton));
+
+ expect(getByText('Mock Flyout')).toBeInTheDocument();
+ });
+
+ it('renders the investigation guide when one is provided', async () => {
+ // provide actual markdown and test it's getting rendered properly
+ const mockMarkdown =
+ '## This is an investigation guide\n\nCall **The team** to resolve _any issues_.\n';
+ const mockRule = { id: 'mock' };
+
+ const { getByRole } = render(
+ {}}
+ refetch={() => {}}
+ // @ts-expect-error internal hook call is mocked, do not need real values
+ rule={mockRule}
+ blob={mockMarkdown}
+ />
+ );
+
+ // test that the component is rendering markdown
+ expect(getByRole('heading', { name: 'This is an investigation guide' }));
+ expect(getByRole('strong')).toHaveTextContent('The team');
+ expect(getByRole('emphasis')).toHaveTextContent('any issues');
+ });
+});
diff --git a/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/investigation_guide.tsx b/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/investigation_guide.tsx
new file mode 100644
index 0000000000000..acaaddbce6b93
--- /dev/null
+++ b/x-pack/solutions/observability/plugins/observability/public/pages/alert_details/components/investigation_guide.tsx
@@ -0,0 +1,83 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { EuiEmptyPrompt, EuiButton, EuiSpacer, EuiMarkdownFormat } from '@elastic/eui';
+import { css } from '@emotion/react';
+import { FormattedMessage } from '@kbn/i18n-react';
+import { RuleFormStepId } from '@kbn/response-ops-rule-form/src/constants';
+import React, { useState } from 'react';
+import {
+ AlertDetailsRuleFormFlyout,
+ type AlertDetailsRuleFormFlyoutBaseProps,
+} from './AlertDetailsRuleFormFlyout';
+
+interface InvestigationGuideProps extends AlertDetailsRuleFormFlyoutBaseProps {
+ blob?: string;
+}
+
+export function InvestigationGuide({ blob, onUpdate, refetch, rule }: InvestigationGuideProps) {
+ const [alertDetailsRuleFormFlyoutOpen, setAlertDetailsRuleFormFlyoutOpen] = useState(false);
+ return blob ? (
+ <>
+
+
+ {blob}
+
+ >
+ ) : (
+ <>
+
+
+
+ }
+ titleSize="m"
+ body={
+
+
+
+ }
+ actions={
+ setAlertDetailsRuleFormFlyoutOpen(true)}
+ fill
+ >
+
+
+ }
+ />
+ {!!rule && (
+
+ )}
+ >
+ );
+}