Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solution] Adds tour for new upgrade flyout diff features #176767

Merged
merged 10 commits into from
Feb 14, 2024
2 changes: 1 addition & 1 deletion x-pack/plugins/security_solution/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ export const RULES_TABLE_MAX_PAGE_SIZE = 100;
* we will need to update these constants with the corresponding version.
*/
export const NEW_FEATURES_TOUR_STORAGE_KEYS = {
RULE_MANAGEMENT_PAGE: 'securitySolution.rulesManagementPage.newFeaturesTour.v8.11',
RULE_MANAGEMENT_PAGE: 'securitySolution.rulesManagementPage.newFeaturesTour.v8.13',
TIMELINES: 'securitySolution.security.timelineFlyoutHeader.saveTimelineTour',
TIMELINE: 'securitySolution.timeline.newFeaturesTour.v8.12',
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const TabNavigationItemComponent = ({
href={appHref}
onClick={handleClick}
append={isBeta && <EuiBadge color={'#E0E5EE'}>{betaOptions?.text ?? BETA}</EuiBadge>}
id={id}
>
{name}
</EuiTab>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,6 @@ export const EXPAND_UNCHANGED_LINES = (linesCount: number) =>
}
);

export const BASE_VERSION = i18n.translate(
'xpack.securitySolution.detectionEngine.rules.upgradeRules.baseVersionLabel',
{
defaultMessage: 'Base version',
}
);

export const BASE_VERSION_DESCRIPTION = i18n.translate(
'xpack.securitySolution.detectionEngine.rules.upgradeRules.baseVersionDescriptionLabel',
{
defaultMessage: 'Shows currently installed rule',
}
);

export const CURRENT_RULE_VERSION = i18n.translate(
'xpack.securitySolution.detectionEngine.rules.upgradeRules.currentVersionLabel',
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ interface RuleDetailsFlyoutProps {
size?: EuiFlyoutProps['size'];
extraTabs?: EuiTabbedContentTab[];
dataTestSubj?: string;
id?: string;
closeFlyout: () => void;
}

Expand All @@ -118,6 +119,7 @@ export const RuleDetailsFlyout = ({
size = 'm',
extraTabs = [],
dataTestSubj,
id,
closeFlyout,
}: RuleDetailsFlyoutProps) => {
const { expandedOverviewSections, toggleOverviewSection } = useOverviewTabSections();
Expand Down Expand Up @@ -181,6 +183,7 @@ export const RuleDetailsFlyout = ({

return (
<EuiFlyout
id={id}
size={size}
onClose={closeFlyout}
ownFocus={false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,13 +150,13 @@ export const RuleDiffTab = ({ oldRule, newRule }: RuleDiffTabProps) => {
<EuiFlexGroup alignItems="baseline" gutterSize="xs">
<EuiIconTip
color="subdued"
content={i18n.BASE_VERSION_DESCRIPTION}
content={i18n.CURRENT_VERSION_DESCRIPTION}
type="iInCircle"
size="m"
display="block"
/>
<EuiTitle size="xxxs">
<h6>{i18n.BASE_VERSION}</h6>
<h6>{i18n.CURRENT_RULE_VERSION}</h6>
</EuiTitle>
</EuiFlexGroup>
<EuiFlexGroup alignItems="baseline" gutterSize="xs">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,19 @@ import {
} from '@elastic/eui';
import { noop } from 'lodash';
import type { FC } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import React, { useEffect, useMemo } from 'react';
import { NEW_FEATURES_TOUR_STORAGE_KEYS } from '../../../../../../common/constants';
import { useKibana } from '../../../../../common/lib/kibana';
import { useIsElementMounted } from '../rules_table/guided_onboarding/use_is_element_mounted';
import { PREBUILT_RULE_UPDATE_FLYOUT_ANCHOR } from '../upgrade_prebuilt_rules_table/upgrade_prebuilt_rules_table_context';
import * as i18n from './translations';

export interface RulesFeatureTourContextType {
steps: EuiTourStepProps[];
actions: EuiTourActions;
}

export const SEARCH_CAPABILITIES_TOUR_ANCHOR = 'search-capabilities-tour-anchor';
export const PER_FIELD_UPGRADE_TOUR_ANCHOR = 'updates';

const TOUR_STORAGE_KEY = NEW_FEATURES_TOUR_STORAGE_KEYS.RULE_MANAGEMENT_PAGE;
const TOUR_POPOVER_WIDTH = 400;
Expand All @@ -41,22 +43,22 @@ const tourConfig: EuiTourState = {
currentTourStep: 1,
isTourActive: true,
tourPopoverWidth: TOUR_POPOVER_WIDTH,
tourSubtitle: i18n.TOUR_TITLE,
tourSubtitle: '',
};

const stepsConfig: EuiStatelessTourStep[] = [
{
step: 1,
title: i18n.SEARCH_CAPABILITIES_TITLE,
content: <EuiText>{i18n.SEARCH_CAPABILITIES_DESCRIPTION}</EuiText>,
title: i18n.UPDATE_TOUR_TITLE,
content: <EuiText>{i18n.UPDATE_TOUR_DESCRIPTION}</EuiText>,
stepsTotal: 1,
children: <></>,
onFinish: noop,
maxWidth: TOUR_POPOVER_WIDTH,
},
];

export const RulesFeatureTour: FC = () => {
export const RuleFeatureTour: FC = () => {
const { storage } = useKibana().services;

const restoredState = useMemo<EuiTourState>(
Expand All @@ -74,28 +76,9 @@ export const RulesFeatureTour: FC = () => {
storage.set(TOUR_STORAGE_KEY, { isTourActive, currentTourStep });
}, [tourState, storage]);

const [shouldShowSearchCapabilitiesTour, setShouldShowSearchCapabilitiesTour] = useState(false);

useEffect(() => {
/**
* 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 observer = new MutationObserver(() => {
if (document.querySelector(`#${SEARCH_CAPABILITIES_TOUR_ANCHOR}`)) {
setShouldShowSearchCapabilitiesTour(true);
observer.disconnect();
}
});

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

return () => observer.disconnect();
}, []);
Comment on lines -77 to -98
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replacement of this block with const isTourAnchorMounted = useIsElementMounted(PER_FIELD_UPGRADE_TOUR_ANCHOR); is very nice 👍

const isTourAnchorMounted = useIsElementMounted(PER_FIELD_UPGRADE_TOUR_ANCHOR);
const isFlyoutOpen = useIsElementMounted(PREBUILT_RULE_UPDATE_FLYOUT_ANCHOR);
const shouldShowRuleUpgradeTour = isTourAnchorMounted && !isFlyoutOpen;

const enhancedSteps = useMemo(
() =>
Expand Down Expand Up @@ -135,7 +118,7 @@ export const RulesFeatureTour: FC = () => {
[tourSteps, tourActions]
);

return shouldShowSearchCapabilitiesTour ? (
return shouldShowRuleUpgradeTour ? (
<EuiTourStep
{...enhancedSteps[0]}
/**
Expand All @@ -144,7 +127,7 @@ export const RulesFeatureTour: FC = () => {
*/
// eslint-disable-next-line react/no-children-prop
children={undefined}
anchor={`#${SEARCH_CAPABILITIES_TOUR_ANCHOR}`}
anchor={`#${PER_FIELD_UPGRADE_TOUR_ANCHOR}`}
anchorPosition="downCenter"
/>
) : null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@ export const NEXT_STEP_LABEL = i18n.translate(
}
);

export const SEARCH_CAPABILITIES_TITLE = i18n.translate(
'xpack.securitySolution.detectionEngine.rules.allRules.featureTour.searchCapabilitiesTitle',
export const UPDATE_TOUR_TITLE = i18n.translate(
'xpack.securitySolution.detectionEngine.ruleDetails.updateTourTitle',
{
defaultMessage: 'Enhanced search capabilities',
defaultMessage: 'New field view of updates',
}
);

export const SEARCH_CAPABILITIES_DESCRIPTION = i18n.translate(
'xpack.securitySolution.detectionEngine.rules.allRules.featureTour.searchCapabilitiesDescription',
export const UPDATE_TOUR_DESCRIPTION = i18n.translate(
'xpack.securitySolution.detectionEngine.ruleDetails.updateTourDescription',
{
defaultMessage:
'It is now possible to search rules by index patterns, like "filebeat-*", or by MITRE ATT&CK™ tactics or techniques, like "Defense Evasion" or "TA0005".',
'You can now view the diff of updates in the flyout by clicking on the rule name.',
dplumlee marked this conversation as resolved.
Show resolved Hide resolved
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,17 @@ export const UPDATE_BUTTON_LABEL = i18n.translate(
defaultMessage: 'Update',
}
);

export const UPDATE_FLYOUT_PER_FIELD_TOOLTIP_DESCRIPTION = i18n.translate(
'xpack.securitySolution.detectionEngine.ruleDetails.perFieldTooltip',
{
defaultMessage: 'View updates on a per field basis.',
dplumlee marked this conversation as resolved.
Show resolved Hide resolved
}
);

export const UPDATE_FLYOUT_JSON_VIEW_TOOLTIP_DESCRIPTION = i18n.translate(
'xpack.securitySolution.detectionEngine.ruleDetails.jsonViewTooltip',
{
defaultMessage: 'View the rule updates in JSON format.',
}
);
banderror marked this conversation as resolved.
Show resolved Hide resolved
dplumlee marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import type { Dispatch, SetStateAction } from 'react';
import React, { createContext, useCallback, useContext, useMemo, useState } from 'react';
import { EuiButton } from '@elastic/eui';
import { EuiButton, EuiToolTip } from '@elastic/eui';
import type { EuiTabbedContentTab } from '@elastic/eui';
import { PerFieldRuleDiffTab } from '../../../../rule_management/components/rule_details/per_field_rule_diff_tab';
import { useIsUpgradingSecurityPackages } from '../../../../rule_management/logic/use_upgrade_security_packages';
Expand Down Expand Up @@ -85,6 +85,8 @@ export interface UpgradePrebuiltRulesTableState {
selectedRules: RuleUpgradeInfoForReview[];
}

export const PREBUILT_RULE_UPDATE_FLYOUT_ANCHOR = 'updatePrebuiltRulePreview';

export interface UpgradePrebuiltRulesTableActions {
reFetchRules: () => void;
upgradeOneRule: (ruleId: string) => void;
Expand Down Expand Up @@ -284,7 +286,14 @@ export const UpgradePrebuiltRulesTableContextProvider = ({
? [
{
id: 'updates',
name: ruleDetailsI18n.UPDATES_TAB_LABEL,
name: (
<EuiToolTip
position="top"
content={i18n.UPDATE_FLYOUT_PER_FIELD_TOOLTIP_DESCRIPTION}
>
<>{ruleDetailsI18n.UPDATES_TAB_LABEL}</>
</EuiToolTip>
),
content: (
<TabContentPadding>
<PerFieldRuleDiffTab ruleDiff={activeRule.diff} />
Expand All @@ -297,7 +306,14 @@ export const UpgradePrebuiltRulesTableContextProvider = ({
? [
{
id: 'jsonViewUpdates',
name: ruleDetailsI18n.JSON_VIEW_UPDATES_TAB_LABEL,
name: (
<EuiToolTip
position="top"
content={i18n.UPDATE_FLYOUT_JSON_VIEW_TOOLTIP_DESCRIPTION}
>
<>{ruleDetailsI18n.JSON_VIEW_UPDATES_TAB_LABEL}</>
</EuiToolTip>
),
content: (
<TabContentPadding>
<RuleDiffTab oldRule={activeRule.current_rule} newRule={activeRule.target_rule} />
Expand Down Expand Up @@ -329,6 +345,7 @@ export const UpgradePrebuiltRulesTableContextProvider = ({
<RuleDetailsFlyout
rule={previewedRule}
size={isJsonPrebuiltRulesDiffingEnabled ? 'l' : 'm'}
id={PREBUILT_RULE_UPDATE_FLYOUT_ANCHOR}
dataTestSubj="updatePrebuiltRulePreview"
closeFlyout={closeRulePreview}
ruleActions={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/
import { MaintenanceWindowCallout } from '@kbn/alerts-ui-shared';
import React, { useCallback } from 'react';
import { DEFAULT_APP_CATEGORIES } from '@kbn/core-application-common';
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
import { APP_UI_ID } from '../../../../../common/constants';
import { SecurityPageName } from '../../../../app/types';
import { ImportDataModal } from '../../../../common/components/import_data_modal';
Expand All @@ -35,6 +36,7 @@ import { AllRules } from '../../components/rules_table';
import { RulesTableContextProvider } from '../../components/rules_table/rules_table/rules_table_context';
import { useInvalidateFetchCoverageOverviewQuery } from '../../../rule_management/api/hooks/use_fetch_coverage_overview_query';
import { HeaderPage } from '../../../../common/components/header_page';
import { RuleFeatureTour } from '../../components/rules_table/feature_tour/rules_feature_tour';

const RulesPageComponent: React.FC = () => {
const [isImportModalVisible, showImportModal, hideImportModal] = useBoolState();
Expand All @@ -53,6 +55,9 @@ const RulesPageComponent: React.FC = () => {
invalidateFetchRuleManagementFilters,
invalidateFetchCoverageOverviewQuery,
]);
const isPerFieldPrebuiltRulesDiffingEnabled = useIsExperimentalFeatureEnabled(
'perFieldPrebuiltRulesDiffingEnabled'
);

const [
{
Expand Down Expand Up @@ -172,6 +177,7 @@ const RulesPageComponent: React.FC = () => {
kibanaServices={kibanaServices}
categories={[DEFAULT_APP_CATEGORIES.security.id]}
/>
{isPerFieldPrebuiltRulesDiffingEnabled && <RuleFeatureTour />}
<AllRules data-test-subj="all-rules" />
</SecuritySolutionPageWrapper>
</RulesTableContextProvider>
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/translations/translations/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -33552,8 +33552,6 @@
"xpack.securitySolution.detectionEngine.rules.allRules.exportFilenameTitle": "rules_export",
"xpack.securitySolution.detectionEngine.rules.allRules.featureTour.nextStepLabel": "Aller à l'étape suivante",
"xpack.securitySolution.detectionEngine.rules.allRules.featureTour.previousStepLabel": "Revenir à l'étape précédente",
"xpack.securitySolution.detectionEngine.rules.allRules.featureTour.searchCapabilitiesDescription": "Il est maintenant possible de rechercher des règles par modèle d'indexation, tel que \"filebeat-*\", ou par tactique ou technique MITRE ATT&CK™, telle que \"Évasion par la défense \" ou \"TA0005\".",
"xpack.securitySolution.detectionEngine.rules.allRules.featureTour.searchCapabilitiesTitle": "Capacités de recherche améliorées",
"xpack.securitySolution.detectionEngine.rules.allRules.featureTour.tourTitle": "Nouveautés",
"xpack.securitySolution.detectionEngine.rules.allRules.filters.customRulesTitle": "Règles personnalisées",
"xpack.securitySolution.detectionEngine.rules.allRules.filters.disabledRulesTitle": "Règles désactivées",
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -33552,8 +33552,6 @@
"xpack.securitySolution.detectionEngine.rules.allRules.exportFilenameTitle": "rules_export",
"xpack.securitySolution.detectionEngine.rules.allRules.featureTour.nextStepLabel": "次のステップに進む",
"xpack.securitySolution.detectionEngine.rules.allRules.featureTour.previousStepLabel": "前のステップに戻る",
"xpack.securitySolution.detectionEngine.rules.allRules.featureTour.searchCapabilitiesDescription": "「filebeat-*」などのインデックスパターンや、「Defense Evasion」や「TA0005」などのMITRE ATT&CK™方式または手法でルールを検索できます。",
"xpack.securitySolution.detectionEngine.rules.allRules.featureTour.searchCapabilitiesTitle": "拡張検索機能",
"xpack.securitySolution.detectionEngine.rules.allRules.featureTour.tourTitle": "新機能",
"xpack.securitySolution.detectionEngine.rules.allRules.filters.customRulesTitle": "カスタムルール",
"xpack.securitySolution.detectionEngine.rules.allRules.filters.disabledRulesTitle": "無効なルール",
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -33534,8 +33534,6 @@
"xpack.securitySolution.detectionEngine.rules.allRules.exportFilenameTitle": "rules_export",
"xpack.securitySolution.detectionEngine.rules.allRules.featureTour.nextStepLabel": "前往下一步",
"xpack.securitySolution.detectionEngine.rules.allRules.featureTour.previousStepLabel": "前往上一步",
"xpack.securitySolution.detectionEngine.rules.allRules.featureTour.searchCapabilitiesDescription": "现在可以按搜索模式(如“filebeat-*”) 或者 MITRE ATT&CK™ 策略或技术(如“Defense Evasion”或“TA0005”)搜索规则。",
"xpack.securitySolution.detectionEngine.rules.allRules.featureTour.searchCapabilitiesTitle": "已增强搜索功能",
"xpack.securitySolution.detectionEngine.rules.allRules.featureTour.tourTitle": "最新动态",
"xpack.securitySolution.detectionEngine.rules.allRules.filters.customRulesTitle": "定制规则",
"xpack.securitySolution.detectionEngine.rules.allRules.filters.disabledRulesTitle": "已禁用规则",
Expand Down