Skip to content

Commit

Permalink
Revert "[Security solution] Guided onboarding rules (#1)"
Browse files Browse the repository at this point in the history
This reverts commit 2bb0b7d56412480af04065912d2be7b8471637df.
  • Loading branch information
xcrzx committed Oct 31, 2022
1 parent 7fd258c commit 37bbf51
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,14 @@ import { securityTourConfig, SecurityStepId } from './tour_config';
export interface TourContextValue {
activeStep: number;
endTourStep: (stepId: SecurityStepId) => void;
incrementStep: (stepId: SecurityStepId) => void;
setActiveStep: (stepId: SecurityStepId, step: number) => void;
incrementStep: (stepId: SecurityStepId, step?: number) => void;
isTourShown: (stepId: SecurityStepId) => boolean;
}

const initialState: TourContextValue = {
activeStep: 0,
endTourStep: () => {},
incrementStep: () => {},
setActiveStep: () => {},
isTourShown: () => false,
};

Expand Down Expand Up @@ -65,12 +63,6 @@ export const RealTourContextProvider = ({ children }: { children: ReactChild })
const isTourShown = useCallback((stepId: SecurityStepId) => tourStatus[stepId], [tourStatus]);
const [activeStep, _setActiveStep] = useState<number>(1);

const setActiveStep = useCallback((stepId: SecurityStepId, step: number) => {
if (step <= securityTourConfig[stepId].length) {
_setActiveStep(step);
}
}, []);

const incrementStep = useCallback((stepId: SecurityStepId) => {
_setActiveStep(
(prevState) => (prevState >= securityTourConfig[stepId].length ? 0 : prevState) + 1
Expand Down Expand Up @@ -113,7 +105,6 @@ export const RealTourContextProvider = ({ children }: { children: ReactChild })
endTourStep,
incrementStep,
isTourShown,
setActiveStep,
};

return <TourContext.Provider value={context}>{children}</TourContext.Provider>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,39 +125,6 @@ const alertsCasesConfig: StepConfig[] = [
},
];

const rulesConfig: StepConfig[] = [
{
...defaultConfig,
title: i18n.translate(
'xpack.securitySolution.detectionEngine.rules.guidedOnboarding.installPrebuiltRules.title',
{ defaultMessage: 'Load the Elastic prebuilt rules' }
),
content: i18n.translate(
'xpack.securitySolution.detectionEngine.rules.guidedOnboarding.installPrebuiltRules.content',
{ defaultMessage: 'To get started you need to load the Elastic prebuilt rules.' }
),
step: 1,
anchorPosition: 'downCenter',
hideNextButton: true,
dataTestSubj: getTourAnchor(1, SecurityStepId.rules),
},
{
...defaultConfig,
title: i18n.translate(
'xpack.securitySolution.detectionEngine.rules.guidedOnboarding.searchFirstRule.title',
{ defaultMessage: 'Search for Elastic Defend rules' }
),
content: i18n.translate(
'xpack.securitySolution.detectionEngine.rules.guidedOnboarding.searchFirstRule.content',
{ defaultMessage: 'Find the rules you want and enable them.' }
),
step: 2,
anchorPosition: 'upCenter',
hideNextButton: true,
dataTestSubj: getTourAnchor(2, SecurityStepId.rules),
},
];

interface SecurityTourConfig {
[SecurityStepId.rules]: StepConfig[];
[SecurityStepId.alertsCases]: StepConfig[];
Expand All @@ -167,6 +134,6 @@ export const securityTourConfig: SecurityTourConfig = {
/**
* D&R team implement your tour config here
*/
[SecurityStepId.rules]: rulesConfig,
[SecurityStepId.rules]: [],
[SecurityStepId.alertsCases]: alertsCasesConfig,
};

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* 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 && (
<EuiTourStep
title={i18n.INSTALL_PREBUILT_RULES_TITLE}
content={i18n.INSTALL_PREBUILT_RULES_CONTENT}
onFinish={noop}
step={1}
stepsTotal={2}
isOpen={tourStatus === GuidedOnboardingRulesStatus.installRules}
anchor={`#${INSTALL_PREBUILT_RULES_ANCHOR}`}
anchorPosition="downCenter"
footerAction={<div />} // Replace "Skip tour" with an empty element
/>
)}
{isSearchFirstRuleAnchorMounted && (
<EuiTourStep
title={i18n.SEARCH_FIRST_RULE_TITLE}
content={i18n.SEARCH_FIRST_RULE_CONTENT}
onFinish={noop}
step={2}
stepsTotal={2}
isOpen={tourStatus === GuidedOnboardingRulesStatus.activateRules}
anchor={`#${SEARCH_FIRST_RULE_ANCHOR}`}
anchorPosition="upCenter"
footerAction={<div />} // Replace "Skip tour" with an empty element
/>
)}
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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.',
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* 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 { useEffect, useState } from 'react';

export const useIsElementMounted = (elementId: string) => {
const [isElementMounted, setIsElementMounted] = useState(false);

useEffect(() => {
const observer = new MutationObserver(() => {
const isElementFound = !!document.getElementById(elementId);

if (isElementFound && !isElementMounted) {
setIsElementMounted(true);
}

if (!isElementFound && isElementMounted) {
setIsElementMounted(false);
}
});

observer.observe(document.body, {
childList: true,
subtree: true,
});

return () => observer.disconnect();
}, [isElementMounted, elementId]);

return isElementMounted;
};
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@ import {
import { isEqual } from 'lodash/fp';
import React, { useCallback } from 'react';
import styled from 'styled-components';
import { GuidedOnboardingTourStep } from '../../../../../common/components/guided_onboarding_tour/tour_step';
import { SecurityStepId } from '../../../../../common/components/guided_onboarding_tour/tour_config';
import { RULES_TABLE_ACTIONS } from '../../../../../common/lib/apm/user_actions';
import { useStartTransaction } from '../../../../../common/lib/apm/use_start_transaction';
import { usePrePackagedRulesStatus } from '../../../../rule_management/logic/use_pre_packaged_rules_status';
import * as i18n from '../../../../../detections/pages/detection_engine/rules/translations';
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';

const FilterWrapper = styled(EuiFlexGroup)`
margin-bottom: ${({ theme }) => theme.eui.euiSizeXS};
Expand Down Expand Up @@ -86,15 +85,14 @@ const RulesTableFiltersComponent = () => {
return (
<FilterWrapper gutterSize="m" justifyContent="flexEnd">
<SearchBarWrapper grow>
<GuidedOnboardingTourStep step={2} stepId={SecurityStepId.rules}>
<EuiFieldSearch
aria-label={i18n.SEARCH_RULES}
fullWidth
incremental={false}
placeholder={i18n.SEARCH_PLACEHOLDER}
onSearch={handleOnSearch}
/>
</GuidedOnboardingTourStep>
<EuiFieldSearch
id={SEARCH_FIRST_RULE_ANCHOR}
aria-label={i18n.SEARCH_RULES}
fullWidth
incremental={false}
placeholder={i18n.SEARCH_PLACEHOLDER}
onSearch={handleOnSearch}
/>
</SearchBarWrapper>
<EuiFlexItem grow={false}>
<EuiFilterGroup>
Expand Down
Loading

0 comments on commit 37bbf51

Please sign in to comment.