diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/guided_onboarding/rules_management_tour.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/guided_onboarding/rules_management_tour.tsx
deleted file mode 100644
index 1fcb56a009edbe..00000000000000
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/guided_onboarding/rules_management_tour.tsx
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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 type { EuiTourActions, EuiTourStepProps } from '@elastic/eui';
-import { EuiTourStep } from '@elastic/eui';
-import { noop } from 'lodash';
-import React, { useEffect, useMemo } from 'react';
-import useObservable from 'react-use/lib/useObservable';
-import { of } from 'rxjs';
-import { useKibana } from '../../../../common/lib/kibana';
-import { useFindRulesQuery } from '../../../rule_management/api/hooks/use_find_rules_query';
-import * as i18n from './translations';
-import { useIsElementMounted } from './use_is_element_mounted';
-
-export const INSTALL_PREBUILT_RULES_ANCHOR = 'install-prebuilt-rules-anchor';
-export const SEARCH_FIRST_RULE_ANCHOR = 'search-first-rule-anchor';
-
-export interface RulesFeatureTourContextType {
- steps: EuiTourStepProps[];
- actions: EuiTourActions;
-}
-
-const GUIDED_ONBOARDING_RULES_FILTER = {
- filter: '',
- showCustomRules: false,
- showElasticRules: true,
- tags: ['Guided Onboarding'],
-};
-
-export enum GuidedOnboardingRulesStatus {
- 'inactive' = 'inactive',
- 'installRules' = 'installRules',
- 'activateRules' = 'activateRules',
- 'completed' = 'completed',
-}
-
-export const RulesManagementTour = () => {
- const { guidedOnboardingApi } = useKibana().services.guidedOnboarding;
-
- const isRulesStepActive = useObservable(
- guidedOnboardingApi?.isGuideStepActive$('security', 'rules') ?? of(false),
- false
- );
-
- const { data: onboardingRules } = useFindRulesQuery(
- { filterOptions: GUIDED_ONBOARDING_RULES_FILTER },
- { enabled: isRulesStepActive }
- );
-
- const tourStatus = useMemo(() => {
- if (!isRulesStepActive || !onboardingRules) {
- return GuidedOnboardingRulesStatus.inactive;
- }
-
- if (onboardingRules.total === 0) {
- // Onboarding rules are not installed - show the install/update rules step
- return GuidedOnboardingRulesStatus.installRules;
- }
-
- if (!onboardingRules.rules.some((rule) => rule.enabled)) {
- // None of the onboarding rules is active - show the activate step
- return GuidedOnboardingRulesStatus.activateRules;
- }
-
- // Rules are installed and enabled - the tour is completed
- return GuidedOnboardingRulesStatus.completed;
- }, [isRulesStepActive, onboardingRules]);
-
- // Synchronize the current "internal" tour step with the global one
- useEffect(() => {
- if (isRulesStepActive && tourStatus === GuidedOnboardingRulesStatus.completed) {
- guidedOnboardingApi?.completeGuideStep('security', 'rules');
- }
- }, [guidedOnboardingApi, isRulesStepActive, tourStatus]);
-
- /**
- * Wait until the tour target elements are visible on the page and mount
- * EuiTourStep components only after that. Otherwise, the tours would never
- * show up on the page.
- */
- const isInstallRulesAnchorMounted = useIsElementMounted(INSTALL_PREBUILT_RULES_ANCHOR);
- const isSearchFirstRuleAnchorMounted = useIsElementMounted(SEARCH_FIRST_RULE_ANCHOR);
-
- return (
- <>
- {isInstallRulesAnchorMounted && (
- } // Replace "Skip tour" with an empty element
- />
- )}
- {isSearchFirstRuleAnchorMounted && (
- } // Replace "Skip tour" with an empty element
- />
- )}
- >
- );
-};
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/guided_onboarding/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/guided_onboarding/translations.ts
deleted file mode 100644
index 6c8a2880801a6b..00000000000000
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/guided_onboarding/translations.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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 { i18n } from '@kbn/i18n';
-
-export const INSTALL_PREBUILT_RULES_TITLE = i18n.translate(
- 'xpack.securitySolution.detectionEngine.rules.guidedOnboarding.installPrebuiltRules.title',
- {
- defaultMessage: 'Load the Elastic prebuilt rules',
- }
-);
-
-export const INSTALL_PREBUILT_RULES_CONTENT = i18n.translate(
- 'xpack.securitySolution.detectionEngine.rules.guidedOnboarding.installPrebuiltRules.content',
- {
- defaultMessage: 'To get started you need to load the Elastic prebuilt rules.',
- }
-);
-
-export const SEARCH_FIRST_RULE_TITLE = i18n.translate(
- 'xpack.securitySolution.detectionEngine.rules.guidedOnboarding.searchFirstRule.title',
- {
- defaultMessage: 'Search for Elastic Defend rules',
- }
-);
-
-export const SEARCH_FIRST_RULE_CONTENT = i18n.translate(
- 'xpack.securitySolution.detectionEngine.rules.guidedOnboarding.searchFirstRule.content',
- {
- defaultMessage: 'Find the My First Alert rule and enable it.',
- }
-);
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/index.tsx
index 5e935083399809..d19fa01dc86107 100644
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/index.tsx
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/index.tsx
@@ -7,6 +7,7 @@
import { EuiSpacer } from '@elastic/eui';
import React, { useState } from 'react';
+import { RulesManagementTour } from './rules_table/guided_onboarding/rules_management_tour';
import { RulesTables } from './rules_tables';
import { AllRulesTabs, RulesTableToolbar } from './rules_table_toolbar';
@@ -23,6 +24,7 @@ export const AllRules = React.memo(() => {
return (
<>
+
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/guided_onboarding/rules_management_tour.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/guided_onboarding/rules_management_tour.tsx
new file mode 100644
index 00000000000000..6a43ba92a9dd74
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/guided_onboarding/rules_management_tour.tsx
@@ -0,0 +1,180 @@
+/*
+ * 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 type { EuiTourActions, EuiTourStepProps } from '@elastic/eui';
+import { EuiButton, EuiTourStep } from '@elastic/eui';
+import { noop } from 'lodash';
+import React, { useCallback, useEffect, useMemo } from 'react';
+import useObservable from 'react-use/lib/useObservable';
+import { of } from 'rxjs';
+import { BulkActionType } from '../../../../../../../common/detection_engine/rule_management/api/rules/bulk_actions/request_schema';
+import { useKibana } from '../../../../../../common/lib/kibana';
+import { useFindRulesQuery } from '../../../../../rule_management/api/hooks/use_find_rules_query';
+import { useExecuteBulkAction } from '../../../../../rule_management/logic/bulk_actions/use_execute_bulk_action';
+import { useRulesTableContext } from '../rules_table_context';
+import * as i18n from './translations';
+import { useIsElementMounted } from './use_is_element_mounted';
+
+export const INSTALL_PREBUILT_RULES_ANCHOR = 'install-prebuilt-rules-anchor';
+export const SEARCH_FIRST_RULE_ANCHOR = 'search-first-rule-anchor';
+
+export interface RulesFeatureTourContextType {
+ steps: EuiTourStepProps[];
+ actions: EuiTourActions;
+}
+
+const GUIDED_ONBOARDING_RULES_FILTER = {
+ filter: '',
+ showCustomRules: false,
+ showElasticRules: true,
+ tags: ['Guided Onboarding'],
+};
+
+export enum GuidedOnboardingRulesStatus {
+ 'inactive' = 'inactive',
+ 'installRules' = 'installRules',
+ 'searchRules' = 'searchRules',
+ 'enableRules' = 'enableRules',
+ 'completed' = 'completed',
+}
+
+export const RulesManagementTour = () => {
+ const { guidedOnboardingApi } = useKibana().services.guidedOnboarding;
+ const { executeBulkAction } = useExecuteBulkAction();
+ const { actions } = useRulesTableContext();
+
+ const isRulesStepActive = useObservable(
+ guidedOnboardingApi?.isGuideStepActive$('security', 'rules') ?? of(false),
+ false
+ );
+
+ const { data: onboardingRules } = useFindRulesQuery(
+ { filterOptions: GUIDED_ONBOARDING_RULES_FILTER },
+ { enabled: isRulesStepActive }
+ );
+
+ const demoRule = useMemo(() => {
+ // Rules are loading, cannot search for rule ID
+ if (!onboardingRules?.rules.length) {
+ return;
+ }
+ // Return any rule, first one is good enough
+ return onboardingRules.rules[0];
+ }, [onboardingRules]);
+
+ const ruleSwitchAnchor = demoRule ? `rule-switch-${demoRule.id}` : '';
+
+ /**
+ * Wait until the tour target elements are visible on the page and mount
+ * EuiTourStep components only after that. Otherwise, the tours would never
+ * show up on the page.
+ */
+ const isInstallRulesAnchorMounted = useIsElementMounted(INSTALL_PREBUILT_RULES_ANCHOR);
+ const isSearchFirstRuleAnchorMounted = useIsElementMounted(SEARCH_FIRST_RULE_ANCHOR);
+ const isActivateFirstRuleAnchorMounted = useIsElementMounted(ruleSwitchAnchor);
+
+ const tourStatus = useMemo(() => {
+ if (!isRulesStepActive || !onboardingRules) {
+ return GuidedOnboardingRulesStatus.inactive;
+ }
+
+ if (onboardingRules.total === 0) {
+ // Onboarding rules are not installed - show the install/update rules step
+ return GuidedOnboardingRulesStatus.installRules;
+ }
+
+ if (demoRule?.enabled) {
+ // Rules are installed and enabled - the tour is completed
+ return GuidedOnboardingRulesStatus.completed;
+ }
+
+ // Rule is installed but not enabled - show the find and activate steps
+ if (isActivateFirstRuleAnchorMounted) {
+ // If rule is visible on the table, show the activation step
+ return GuidedOnboardingRulesStatus.enableRules;
+ } else {
+ // If rule is not visible on the table, show the search step
+ return GuidedOnboardingRulesStatus.searchRules;
+ }
+ }, [demoRule?.enabled, isActivateFirstRuleAnchorMounted, isRulesStepActive, onboardingRules]);
+
+ // Synchronize the current "internal" tour step with the global one
+ useEffect(() => {
+ if (isRulesStepActive && tourStatus === GuidedOnboardingRulesStatus.completed) {
+ guidedOnboardingApi?.completeGuideStep('security', 'rules');
+ }
+ }, [guidedOnboardingApi, isRulesStepActive, tourStatus]);
+
+ const enableDemoRule = useCallback(async () => {
+ if (demoRule) {
+ await executeBulkAction({
+ type: BulkActionType.enable,
+ ids: [demoRule.id],
+ });
+ }
+ }, [demoRule, executeBulkAction]);
+
+ const findDemoRule = useCallback(() => {
+ if (demoRule) {
+ actions.setFilterOptions({
+ filter: demoRule.name,
+ });
+ }
+ }, [actions, demoRule]);
+
+ return (
+ <>
+ {isInstallRulesAnchorMounted && (
+ } // Replace "Skip tour" with an empty element
+ />
+ )}
+ {isSearchFirstRuleAnchorMounted && demoRule && (
+
+ {i18n.NEXT_BUTTON}
+
+ }
+ />
+ )}
+ {isActivateFirstRuleAnchorMounted && demoRule && (
+
+ {i18n.NEXT_BUTTON}
+
+ }
+ />
+ )}
+ >
+ );
+};
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/guided_onboarding/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/guided_onboarding/translations.ts
new file mode 100644
index 00000000000000..8a62c76add5bf1
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/guided_onboarding/translations.ts
@@ -0,0 +1,65 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+
+export const INSTALL_PREBUILT_RULES_TITLE = i18n.translate(
+ 'xpack.securitySolution.detectionEngine.rules.guidedOnboarding.installPrebuiltRules.title',
+ {
+ defaultMessage: 'Load the Elastic prebuilt rules',
+ }
+);
+
+export const INSTALL_PREBUILT_RULES_CONTENT = i18n.translate(
+ 'xpack.securitySolution.detectionEngine.rules.guidedOnboarding.installPrebuiltRules.content',
+ {
+ defaultMessage: 'To get started you need to load the Elastic prebuilt rules.',
+ }
+);
+
+export const SEARCH_FIRST_RULE_TITLE = (name: string) =>
+ i18n.translate(
+ 'xpack.securitySolution.detectionEngine.rules.guidedOnboarding.searchFirstRule.title',
+ {
+ defaultMessage: 'Search for "{name}" rule',
+ values: { name },
+ }
+ );
+
+export const SEARCH_FIRST_RULE_CONTENT = (name: string) =>
+ i18n.translate(
+ 'xpack.securitySolution.detectionEngine.rules.guidedOnboarding.searchFirstRule.content',
+ {
+ defaultMessage: 'Find the "{name}" rule.',
+ values: { name },
+ }
+ );
+
+export const ENABLE_FIRST_RULE_TITLE = (name: string) =>
+ i18n.translate(
+ 'xpack.securitySolution.detectionEngine.rules.guidedOnboarding.enableFirstRule.title',
+ {
+ defaultMessage: 'Enable "{name}" rule',
+ values: { name },
+ }
+ );
+
+export const ENABLE_FIRST_RULE_CONTENT = (name: string) =>
+ i18n.translate(
+ 'xpack.securitySolution.detectionEngine.rules.guidedOnboarding.enableFirstRule.content',
+ {
+ defaultMessage: 'Enable the "{name}" rule.',
+ values: { name },
+ }
+ );
+
+export const NEXT_BUTTON = i18n.translate(
+ 'xpack.securitySolution.detectionEngine.rules.guidedOnboarding.nextButton',
+ {
+ defaultMessage: 'Next',
+ }
+);
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/guided_onboarding/use_is_element_mounted.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/guided_onboarding/use_is_element_mounted.ts
similarity index 100%
rename from x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/guided_onboarding/use_is_element_mounted.ts
rename to x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table/guided_onboarding/use_is_element_mounted.ts
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_filters/rules_table_filters.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_filters/rules_table_filters.tsx
index 143ae37a694d12..34c039c6c9eb74 100644
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_filters/rules_table_filters.tsx
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_filters/rules_table_filters.tsx
@@ -13,7 +13,7 @@ import {
EuiFlexItem,
} from '@elastic/eui';
import { isEqual } from 'lodash/fp';
-import React, { useCallback } from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
import styled from 'styled-components';
import { RULES_TABLE_ACTIONS } from '../../../../../common/lib/apm/user_actions';
import { useStartTransaction } from '../../../../../common/lib/apm/use_start_transaction';
@@ -22,7 +22,7 @@ import * as i18n from '../../../../../detections/pages/detection_engine/rules/tr
import { useRulesTableContext } from '../rules_table/rules_table_context';
import { TagsFilterPopover } from './tags_filter_popover';
import { useTags } from '../../../../rule_management/logic/use_tags';
-import { SEARCH_FIRST_RULE_ANCHOR } from '../../guided_onboarding/rules_management_tour';
+import { SEARCH_FIRST_RULE_ANCHOR } from '../rules_table/guided_onboarding/rules_management_tour';
const FilterWrapper = styled(EuiFlexGroup)`
margin-bottom: ${({ theme }) => theme.eui.euiSizeXS};
@@ -54,6 +54,17 @@ const RulesTableFiltersComponent = () => {
const { showCustomRules, showElasticRules, tags: selectedTags } = filterOptions;
+ const [searchText, setSearchText] = useState(filterOptions.filter);
+
+ useEffect(() => {
+ setSearchText(filterOptions.filter);
+ }, [filterOptions.filter]);
+
+ const handleSearchInputChange = useCallback(
+ (e: React.ChangeEvent) => setSearchText(e.target.value),
+ []
+ );
+
const handleOnSearch = useCallback(
(filterString) => {
startTransaction({ name: RULES_TABLE_ACTIONS.FILTER });
@@ -87,11 +98,13 @@ const RulesTableFiltersComponent = () => {
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/index.tsx
index 16a30acb296519..4409460574368e 100644
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/index.tsx
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/index.tsx
@@ -41,7 +41,6 @@ import { AllRules } from '../../components/rules_table';
import { RulesTableContextProvider } from '../../components/rules_table/rules_table/rules_table_context';
import * as i18n from '../../../../detections/pages/detection_engine/rules/translations';
-import { RulesManagementTour } from '../../components/guided_onboarding/rules_management_tour';
const RulesPageComponent: React.FC = () => {
const [isImportModalVisible, showImportModal, hideImportModal] = useBoolState();
@@ -92,7 +91,6 @@ const RulesPageComponent: React.FC = () => {
-
+
{showLoader ? (