Skip to content

Commit

Permalink
[Security Solution] Adds tour for new upgrade flyout diff features (#…
Browse files Browse the repository at this point in the history
…176767)

## Summary
Issue: #166489

Adds a tour and tooltips highlighting and describing the new diff
features of the prebuilt rule update flyout.

#### To test: 
Enable the `jsonPrebuiltRulesDiffingEnabled` and/or
`perFieldPrebuiltRulesDiffingEnabled` feature flags and clear your
browser of the local storage
`securitySolution.rulesManagementPage.newFeaturesTour.v8.13` token. This
should allow you to see the tour and both new tabs highlighted by
tooltips as shown in the screenshots below.

### Screenshots
**Upgrade tour**
<img width="1406" alt="Screenshot 2024-02-13 at 2 14 07 PM"
src="https://github.com/elastic/kibana/assets/56367316/20d3fd8e-fc39-4ae2-a627-272ae14e9aac">

**Diff tab tooltips**
<img width="1176" alt="Screenshot 2024-02-13 at 2 14 22 PM"
src="https://github.com/elastic/kibana/assets/56367316/8810c17e-4dfb-4758-b4c7-199a44531a4e">



### Checklist

Delete any items that are not applicable to this PR.

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))



### For maintainers

- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
  • Loading branch information
dplumlee authored Feb 14, 2024
1 parent 7666a41 commit db513b7
Show file tree
Hide file tree
Showing 13 changed files with 66 additions and 63 deletions.
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 @@ -451,7 +451,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();
}, []);
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,16 @@ 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".',
defaultMessage: "Click on a rule's name to view the latest changes.",
}
);
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 changes field by field.',
}
);

export const UPDATE_FLYOUT_JSON_VIEW_TOOLTIP_DESCRIPTION = i18n.translate(
'xpack.securitySolution.detectionEngine.ruleDetails.jsonViewTooltip',
{
defaultMessage: 'View the latest rule changes in JSON format.',
}
);
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 @@ -33548,8 +33548,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 @@ -33548,8 +33548,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 @@ -33530,8 +33530,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

0 comments on commit db513b7

Please sign in to comment.