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 @@ -25,6 +25,7 @@ const TabNavigationItemComponent = ({
isSelected,
isBeta,
betaOptions,
tourAnchor,
}: TabNavigationItemProps) => {
const { getAppUrl, navigateTo } = useNavigation();

Expand All @@ -50,6 +51,7 @@ const TabNavigationItemComponent = ({
href={appHref}
onClick={handleClick}
append={isBeta && <EuiBadge color={'#E0E5EE'}>{betaOptions?.text ?? BETA}</EuiBadge>}
id={tourAnchor}
banderror marked this conversation as resolved.
Show resolved Hide resolved
>
{name}
</EuiTab>
Expand Down Expand Up @@ -96,6 +98,7 @@ export const TabNavigationComponent: React.FC<TabNavigationProps> = ({ navTabs }
isSelected={isSelected}
isBeta={tab.isBeta}
betaOptions={tab.betaOptions}
tourAnchor={tab.tourAnchor}
/>
);
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ export interface TabNavigationItemProps {
betaOptions?: {
text: string;
};
tourAnchor?: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,5 @@ export interface NavTab {
betaOptions?: {
text: string;
};
tourAnchor?: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { getPromptContextFromDetectionRules } from '../../../../assistant/helper
import { useRulesTableContext } from './rules_table/rules_table_context';
import { useAssistantAvailability } from '../../../../assistant/use_assistant_availability';
import * as i18nAssistant from '../../../../detections/pages/detection_engine/rules/translations';
import { PER_FIELD_UPGRADE_TOUR_ANCHOR } from './upgrade_prebuilt_rules_table/upgrade_prebuilt_rules_tour';

export enum AllRulesTabs {
management = 'management',
Expand Down Expand Up @@ -70,6 +71,7 @@ export const RulesTableToolbar = React.memo(() => {
betaOptions: {
text: `${updateTotal}`,
},
tourAnchor: PER_FIELD_UPGRADE_TOUR_ANCHOR,
},
}
: {}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,32 @@ export const UPDATE_BUTTON_LABEL = i18n.translate(
defaultMessage: 'Update',
}
);

export const UPDATE_TOUR_TITLE = i18n.translate(
'xpack.securitySolution.detectionEngine.ruleDetails.updateTourTitle',
{
defaultMessage: 'New field view of updates',
}
);

export const UPDATE_TOUR_DESCRIPTION = i18n.translate(
'xpack.securitySolution.detectionEngine.ruleDetails.updateTourDescription',
{
defaultMessage:
'You can now view the diff of updates in the flyout by clicking on the rule name.',
}
);

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 @@ -284,7 +284,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 +304,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
dplumlee marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* 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 {
EuiStatelessTourStep,
EuiTourActions,
EuiTourState,
EuiTourStepProps,
} from '@elastic/eui';
import {
EuiButtonIcon,
EuiFlexGroup,
EuiFlexItem,
EuiSpacer,
EuiText,
EuiTourStep,
useEuiTour,
} from '@elastic/eui';
import { noop } from 'lodash';
import type { FC } 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 * as i18n from './translations';

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

export const PER_FIELD_UPGRADE_TOUR_ANCHOR = 'perFieldUpgradeTour';

const TOUR_STORAGE_KEY = NEW_FEATURES_TOUR_STORAGE_KEYS.RULE_MANAGEMENT_PAGE;
const TOUR_POPOVER_WIDTH = 400;

const tourConfig: EuiTourState = {
currentTourStep: 1,
isTourActive: true,
tourPopoverWidth: TOUR_POPOVER_WIDTH,
tourSubtitle: '',
};

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

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

const restoredState = useMemo<EuiTourState>(
() => ({
...tourConfig,
...storage.get(TOUR_STORAGE_KEY),
}),
[storage]
);

const [tourSteps, tourActions, tourState] = useEuiTour(stepsConfig, restoredState);

useEffect(() => {
const { isTourActive, currentTourStep } = tourState;
storage.set(TOUR_STORAGE_KEY, { isTourActive, currentTourStep });
}, [tourState, storage]);

const shouldShowRuleUpgradeTour = useIsElementMounted(PER_FIELD_UPGRADE_TOUR_ANCHOR);
dplumlee marked this conversation as resolved.
Show resolved Hide resolved

const enhancedSteps = useMemo(
() =>
tourSteps.map((item, index) => ({
...item,
content: (
<>
{item.content}
{tourSteps.length > 1 && (
<>
<EuiSpacer size="s" />
<EuiFlexGroup responsive={false} gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiButtonIcon
iconType="arrowLeft"
aria-label={'previous'}
display="empty"
disabled={index === 0}
onClick={tourActions.decrementStep}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonIcon
iconType="arrowRight"
aria-label={'next'}
display="base"
disabled={index === tourSteps.length - 1}
onClick={tourActions.incrementStep}
/>
</EuiFlexItem>
</EuiFlexGroup>
</>
)}
Copy link
Contributor

Choose a reason for hiding this comment

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

Since the tour only has one step, you don't need all of this code. This can be simply:

  const enhancedSteps = useMemo(
    () =>
      tourSteps.map((item) => ({
        ...item,
        content: item.content,
      })),
    [tourSteps]
  );

Copy link
Contributor Author

@dplumlee dplumlee Feb 13, 2024

Choose a reason for hiding this comment

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

Since we are switching back to just modifying the existing rules_feature_tour.tsx file in a0a9587, I went ahead and left this multi-step logic in the file for a future use case if we do want to add another new feature tour and there are multiple steps.

</>
),
})),
[tourSteps, tourActions]
);

return shouldShowRuleUpgradeTour ? (
<EuiTourStep
{...enhancedSteps[0]}
/**
* children={undefined} is needed to narrow down EuiTourStepProps. Without
* it we get a TS error: Types of property 'anchor' are incompatible.
*/
// eslint-disable-next-line react/no-children-prop
children={undefined}
anchor={`#${PER_FIELD_UPGRADE_TOUR_ANCHOR}`}
anchorPosition="downCenter"
/>
) : null;
};
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,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 { PrebuiltRulesUpgradeTour } from '../../components/rules_table/upgrade_prebuilt_rules_table/upgrade_prebuilt_rules_tour';

const RulesPageComponent: React.FC = () => {
const [isImportModalVisible, showImportModal, hideImportModal] = useBoolState();
Expand Down Expand Up @@ -172,6 +173,7 @@ const RulesPageComponent: React.FC = () => {
kibanaServices={kibanaServices}
categories={[DEFAULT_APP_CATEGORIES.security.id]}
/>
<PrebuiltRulesUpgradeTour />
banderror marked this conversation as resolved.
Show resolved Hide resolved
<AllRules data-test-subj="all-rules" />
</SecuritySolutionPageWrapper>
</RulesTableContextProvider>
Expand Down