mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 10:23:14 -04:00
# Backport This will backport the following commits from `main` to `8.16`: - [[Security Solution] Removes obselete Timeline Tour - New Features (#197385)](https://github.com/elastic/kibana/pull/197385) <!--- Backport version: 9.4.3 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Jatin Kathuria","email":"jatin.kathuria@elastic.co"},"sourceCommit":{"committedDate":"2024-10-25T04:41:35Z","message":"[Security Solution] Removes obselete Timeline Tour - New Features (#197385)\n\n## Summary\r\n\r\nHandles https://github.com/elastic/kibana/issues/197295\r\n\r\nThis PR removes the obselete timeline tour which was introduced in\r\n`8.12` and may not be relevant now in `8.16`.\r\n\r\nFrom the perspective of users directly to `8.16` from `8.11`. I guess it\r\nmight be okay for users to expect considerable changes that may have\r\nhappened between `8.11` and `8.16` and a tour might not be necessary 🤷","sha":"663a339b3a4937682346d5e762da212c4d4e109a","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["backport","release_note:skip","Team:Fleet","v9.0.0","Team:Threat Hunting:Investigations","v8.16.0"],"title":"[Security Solution] Removes obselete Timeline Tour - New Features","number":197385,"url":"https://github.com/elastic/kibana/pull/197385","mergeCommit":{"message":"[Security Solution] Removes obselete Timeline Tour - New Features (#197385)\n\n## Summary\r\n\r\nHandles https://github.com/elastic/kibana/issues/197295\r\n\r\nThis PR removes the obselete timeline tour which was introduced in\r\n`8.12` and may not be relevant now in `8.16`.\r\n\r\nFrom the perspective of users directly to `8.16` from `8.11`. I guess it\r\nmight be okay for users to expect considerable changes that may have\r\nhappened between `8.11` and `8.16` and a tour might not be necessary 🤷","sha":"663a339b3a4937682346d5e762da212c4d4e109a"}},"sourceBranch":"main","suggestedTargetBranches":["8.16"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/197385","number":197385,"mergeCommit":{"message":"[Security Solution] Removes obselete Timeline Tour - New Features (#197385)\n\n## Summary\r\n\r\nHandles https://github.com/elastic/kibana/issues/197295\r\n\r\nThis PR removes the obselete timeline tour which was introduced in\r\n`8.12` and may not be relevant now in `8.16`.\r\n\r\nFrom the perspective of users directly to `8.16` from `8.11`. I guess it\r\nmight be okay for users to expect considerable changes that may have\r\nhappened between `8.11` and `8.16` and a tour might not be necessary 🤷","sha":"663a339b3a4937682346d5e762da212c4d4e109a"}},{"branch":"8.16","label":"v8.16.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: Jatin Kathuria <jatin.kathuria@elastic.co>
This commit is contained in:
parent
9bd2c8accd
commit
71045ee24e
15 changed files with 30 additions and 605 deletions
|
@ -67,7 +67,6 @@ export const internalRequest = <T = unknown>({
|
||||||
const NEW_FEATURES_TOUR_STORAGE_KEYS = {
|
const NEW_FEATURES_TOUR_STORAGE_KEYS = {
|
||||||
RULE_MANAGEMENT_PAGE: 'securitySolution.rulesManagementPage.newFeaturesTour.v8.9',
|
RULE_MANAGEMENT_PAGE: 'securitySolution.rulesManagementPage.newFeaturesTour.v8.9',
|
||||||
TIMELINES: 'securitySolution.security.timelineFlyoutHeader.saveTimelineTour',
|
TIMELINES: 'securitySolution.security.timelineFlyoutHeader.saveTimelineTour',
|
||||||
TIMELINE: 'securitySolution.timeline.newFeaturesTour.v8.12',
|
|
||||||
FLYOUT: 'securitySolution.documentDetails.newFeaturesTour.v8.14',
|
FLYOUT: 'securitySolution.documentDetails.newFeaturesTour.v8.14',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -422,7 +422,6 @@ export const RULES_TABLE_MAX_PAGE_SIZE = 100;
|
||||||
export const NEW_FEATURES_TOUR_STORAGE_KEYS = {
|
export const NEW_FEATURES_TOUR_STORAGE_KEYS = {
|
||||||
RULE_MANAGEMENT_PAGE: 'securitySolution.rulesManagementPage.newFeaturesTour.v8.13',
|
RULE_MANAGEMENT_PAGE: 'securitySolution.rulesManagementPage.newFeaturesTour.v8.13',
|
||||||
TIMELINES: 'securitySolution.security.timelineFlyoutHeader.saveTimelineTour',
|
TIMELINES: 'securitySolution.security.timelineFlyoutHeader.saveTimelineTour',
|
||||||
TIMELINE: 'securitySolution.timeline.newFeaturesTour.v8.12',
|
|
||||||
FLYOUT: 'securitySolution.documentDetails.newFeaturesTour.v8.14',
|
FLYOUT: 'securitySolution.documentDetails.newFeaturesTour.v8.14',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -30,10 +30,10 @@ jest.mock('react-redux', () => {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const renderAddFavoritesButton = (isPartOfGuidedTour = false) =>
|
const renderAddFavoritesButton = () =>
|
||||||
render(
|
render(
|
||||||
<TestProviders>
|
<TestProviders>
|
||||||
<AddToFavoritesButton timelineId="timeline-1" isPartOfGuidedTour={isPartOfGuidedTour} />
|
<AddToFavoritesButton timelineId="timeline-1" />
|
||||||
</TestProviders>
|
</TestProviders>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -87,17 +87,4 @@ describe('AddToFavoritesButton', () => {
|
||||||
expect(getByTestId('timeline-favorite-filled-star')).toBeInTheDocument();
|
expect(getByTestId('timeline-favorite-filled-star')).toBeInTheDocument();
|
||||||
expect(queryByTestId('timeline-favorite-empty-star')).not.toBeInTheDocument();
|
expect(queryByTestId('timeline-favorite-empty-star')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use id for guided tour if prop is true', () => {
|
|
||||||
mockGetState.mockReturnValue({
|
|
||||||
...mockTimelineModel,
|
|
||||||
status: TimelineStatusEnum.active,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { getByTestId } = renderAddFavoritesButton(true);
|
|
||||||
|
|
||||||
const button = getByTestId('timeline-favorite-empty-star');
|
|
||||||
|
|
||||||
expect(button).toHaveProperty('id', 'add-to-favorites');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,7 +13,6 @@ import type { State } from '../../../common/store';
|
||||||
import { selectTimelineById } from '../../store/selectors';
|
import { selectTimelineById } from '../../store/selectors';
|
||||||
import { timelineActions } from '../../store';
|
import { timelineActions } from '../../store';
|
||||||
import { TimelineStatusEnum } from '../../../../common/api/timeline';
|
import { TimelineStatusEnum } from '../../../../common/api/timeline';
|
||||||
import { TIMELINE_TOUR_CONFIG_ANCHORS } from '../timeline/tour/step_config';
|
|
||||||
|
|
||||||
const ADD_TO_FAVORITES = i18n.translate(
|
const ADD_TO_FAVORITES = i18n.translate(
|
||||||
'xpack.securitySolution.timeline.addToFavoriteButtonLabel',
|
'xpack.securitySolution.timeline.addToFavoriteButtonLabel',
|
||||||
|
@ -34,18 +33,13 @@ interface AddToFavoritesButtonProps {
|
||||||
* Id of the timeline to be displayed in the bottom bar and within the modal
|
* Id of the timeline to be displayed in the bottom bar and within the modal
|
||||||
*/
|
*/
|
||||||
timelineId: string;
|
timelineId: string;
|
||||||
/**
|
|
||||||
* Whether the button is a step in the timeline guided tour
|
|
||||||
*/
|
|
||||||
isPartOfGuidedTour?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component renders the add to favorites button for timeline.
|
* This component renders the add to favorites button for timeline.
|
||||||
* It is used in the bottom bar as well as in the timeline modal's header.
|
* It is used in the bottom bar as well as in the timeline modal's header.
|
||||||
*/
|
*/
|
||||||
export const AddToFavoritesButton = React.memo<AddToFavoritesButtonProps>(
|
export const AddToFavoritesButton = React.memo<AddToFavoritesButtonProps>(({ timelineId }) => {
|
||||||
({ timelineId, isPartOfGuidedTour = false }) => {
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { isFavorite, status } = useSelector((state: State) =>
|
const { isFavorite, status } = useSelector((state: State) =>
|
||||||
selectTimelineById(state, timelineId)
|
selectTimelineById(state, timelineId)
|
||||||
|
@ -61,7 +55,6 @@ export const AddToFavoritesButton = React.memo<AddToFavoritesButtonProps>(
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EuiButtonIcon
|
<EuiButtonIcon
|
||||||
id={isPartOfGuidedTour ? TIMELINE_TOUR_CONFIG_ANCHORS.ADD_TO_FAVORITES : undefined}
|
|
||||||
iconType={isFavorite ? 'starFilled' : 'starEmpty'}
|
iconType={isFavorite ? 'starFilled' : 'starEmpty'}
|
||||||
isSelected={isFavorite}
|
isSelected={isFavorite}
|
||||||
disabled={isTimelineDraftOrImmutable}
|
disabled={isTimelineDraftOrImmutable}
|
||||||
|
@ -73,7 +66,6 @@ export const AddToFavoritesButton = React.memo<AddToFavoritesButtonProps>(
|
||||||
{label}
|
{label}
|
||||||
</EuiButtonIcon>
|
</EuiButtonIcon>
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
AddToFavoritesButton.displayName = 'AddToFavoritesButton';
|
AddToFavoritesButton.displayName = 'AddToFavoritesButton';
|
||||||
|
|
|
@ -15,7 +15,6 @@ import { SaveTimelineModal } from './save_timeline_modal';
|
||||||
import * as i18n from './translations';
|
import * as i18n from './translations';
|
||||||
import { selectTimelineById } from '../../../store/selectors';
|
import { selectTimelineById } from '../../../store/selectors';
|
||||||
import type { State } from '../../../../common/store';
|
import type { State } from '../../../../common/store';
|
||||||
import { TIMELINE_TOUR_CONFIG_ANCHORS } from '../../timeline/tour/step_config';
|
|
||||||
|
|
||||||
export interface SaveTimelineButtonProps {
|
export interface SaveTimelineButtonProps {
|
||||||
/**
|
/**
|
||||||
|
@ -75,7 +74,6 @@ export const SaveTimelineButton = React.memo<SaveTimelineButtonProps>(
|
||||||
data-test-subj="timeline-modal-save-timeline-tooltip"
|
data-test-subj="timeline-modal-save-timeline-tooltip"
|
||||||
>
|
>
|
||||||
<EuiButton
|
<EuiButton
|
||||||
id={TIMELINE_TOUR_CONFIG_ANCHORS.SAVE_TIMELINE}
|
|
||||||
fill
|
fill
|
||||||
size="s"
|
size="s"
|
||||||
color={buttonColor}
|
color={buttonColor}
|
||||||
|
|
|
@ -18,7 +18,6 @@ import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { getEsQueryConfig } from '@kbn/data-plugin/common';
|
import { getEsQueryConfig } from '@kbn/data-plugin/common';
|
||||||
import { euiStyled } from '@kbn/kibana-react-plugin/common';
|
import { euiStyled } from '@kbn/kibana-react-plugin/common';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { TIMELINE_TOUR_CONFIG_ANCHORS } from '../../timeline/tour/step_config';
|
|
||||||
import { NewTimelineButton } from '../actions/new_timeline_button';
|
import { NewTimelineButton } from '../actions/new_timeline_button';
|
||||||
import { OpenTimelineButton } from '../actions/open_timeline_button';
|
import { OpenTimelineButton } from '../actions/open_timeline_button';
|
||||||
import { APP_ID } from '../../../../../common';
|
import { APP_ID } from '../../../../../common';
|
||||||
|
@ -125,7 +124,7 @@ export const TimelineModalHeader = React.memo<FlyoutHeaderPanelProps>(
|
||||||
<EuiFlexItem grow={false}>
|
<EuiFlexItem grow={false}>
|
||||||
<EuiFlexGroup gutterSize="xs" alignItems="center" responsive={false}>
|
<EuiFlexGroup gutterSize="xs" alignItems="center" responsive={false}>
|
||||||
<EuiFlexItem grow={false}>
|
<EuiFlexItem grow={false}>
|
||||||
<AddToFavoritesButton timelineId={timelineId} isPartOfGuidedTour />
|
<AddToFavoritesButton timelineId={timelineId} />
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
<EuiFlexItem grow={false}>
|
<EuiFlexItem grow={false}>
|
||||||
<EuiText
|
<EuiText
|
||||||
|
@ -143,7 +142,6 @@ export const TimelineModalHeader = React.memo<FlyoutHeaderPanelProps>(
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
<EuiFlexItem grow={false}>
|
<EuiFlexItem grow={false}>
|
||||||
<EuiFlexGroup
|
<EuiFlexGroup
|
||||||
id={TIMELINE_TOUR_CONFIG_ANCHORS.ACTION_MENU}
|
|
||||||
justifyContent="flexEnd"
|
justifyContent="flexEnd"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
gutterSize="xs"
|
gutterSize="xs"
|
||||||
|
|
|
@ -12,13 +12,12 @@ import { useDispatch, useSelector } from 'react-redux';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { isTab } from '@kbn/timelines-plugin/public';
|
import { isTab } from '@kbn/timelines-plugin/public';
|
||||||
import { useUserPrivileges } from '../../../common/components/user_privileges';
|
|
||||||
import { timelineActions, timelineSelectors } from '../../store';
|
import { timelineActions, timelineSelectors } from '../../store';
|
||||||
import { timelineDefaults } from '../../store/defaults';
|
import { timelineDefaults } from '../../store/defaults';
|
||||||
import type { CellValueElementProps } from './cell_rendering';
|
import type { CellValueElementProps } from './cell_rendering';
|
||||||
import { SourcererScopeName } from '../../../sourcerer/store/model';
|
import { SourcererScopeName } from '../../../sourcerer/store/model';
|
||||||
import { TimelineModalHeader } from '../modal/header';
|
import { TimelineModalHeader } from '../modal/header';
|
||||||
import type { TimelineId, RowRenderer, TimelineTabs } from '../../../../common/types/timeline';
|
import type { TimelineId, RowRenderer } from '../../../../common/types/timeline';
|
||||||
import { TimelineTypeEnum } from '../../../../common/api/timeline';
|
import { TimelineTypeEnum } from '../../../../common/api/timeline';
|
||||||
import { useDeepEqualSelector, useShallowEqualSelector } from '../../../common/hooks/use_selector';
|
import { useDeepEqualSelector, useShallowEqualSelector } from '../../../common/hooks/use_selector';
|
||||||
import type { State } from '../../../common/store';
|
import type { State } from '../../../common/store';
|
||||||
|
@ -30,7 +29,6 @@ import { useTimelineFullScreen } from '../../../common/containers/use_full_scree
|
||||||
import { EXIT_FULL_SCREEN_CLASS_NAME } from '../../../common/components/exit_full_screen';
|
import { EXIT_FULL_SCREEN_CLASS_NAME } from '../../../common/components/exit_full_screen';
|
||||||
import { useResolveConflict } from '../../../common/hooks/use_resolve_conflict';
|
import { useResolveConflict } from '../../../common/hooks/use_resolve_conflict';
|
||||||
import { sourcererSelectors } from '../../../common/store';
|
import { sourcererSelectors } from '../../../common/store';
|
||||||
import { TimelineTour } from './tour';
|
|
||||||
import { defaultUdtHeaders } from './unified_components/default_headers';
|
import { defaultUdtHeaders } from './unified_components/default_headers';
|
||||||
|
|
||||||
const TimelineTemplateBadge = styled.div`
|
const TimelineTemplateBadge = styled.div`
|
||||||
|
@ -90,9 +88,6 @@ const StatefulTimelineComponent: React.FC<Props> = ({
|
||||||
description,
|
description,
|
||||||
sessionViewConfig,
|
sessionViewConfig,
|
||||||
initialized,
|
initialized,
|
||||||
show: isOpen,
|
|
||||||
isLoading,
|
|
||||||
activeTab,
|
|
||||||
} = useDeepEqualSelector((state) =>
|
} = useDeepEqualSelector((state) =>
|
||||||
pick(
|
pick(
|
||||||
[
|
[
|
||||||
|
@ -112,10 +107,6 @@ const StatefulTimelineComponent: React.FC<Props> = ({
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
|
||||||
kibanaSecuritySolutionsPrivileges: { crud: canEditTimeline },
|
|
||||||
} = useUserPrivileges();
|
|
||||||
|
|
||||||
const { timelineFullScreen } = useTimelineFullScreen();
|
const { timelineFullScreen } = useTimelineFullScreen();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -205,20 +196,6 @@ const StatefulTimelineComponent: React.FC<Props> = ({
|
||||||
const timelineContext = useMemo(() => ({ timelineId }), [timelineId]);
|
const timelineContext = useMemo(() => ({ timelineId }), [timelineId]);
|
||||||
const resolveConflictComponent = useResolveConflict();
|
const resolveConflictComponent = useResolveConflict();
|
||||||
|
|
||||||
const showTimelineTour = isOpen && !isLoading && canEditTimeline;
|
|
||||||
|
|
||||||
const handleSwitchToTab = useCallback(
|
|
||||||
(tab: TimelineTabs) => {
|
|
||||||
dispatch(
|
|
||||||
timelineActions.setActiveTabTimeline({
|
|
||||||
id: timelineId,
|
|
||||||
activeTab: tab,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[timelineId, dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TimelineContext.Provider value={timelineContext}>
|
<TimelineContext.Provider value={timelineContext}>
|
||||||
<TimelineContainer
|
<TimelineContainer
|
||||||
|
@ -254,13 +231,6 @@ const StatefulTimelineComponent: React.FC<Props> = ({
|
||||||
/>
|
/>
|
||||||
</TimelineBody>
|
</TimelineBody>
|
||||||
</TimelineContainer>
|
</TimelineContainer>
|
||||||
{showTimelineTour ? (
|
|
||||||
<TimelineTour
|
|
||||||
activeTab={activeTab}
|
|
||||||
switchToTab={handleSwitchToTab}
|
|
||||||
timelineType={timelineType}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</TimelineContext.Provider>
|
</TimelineContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,7 +28,6 @@ import {
|
||||||
DATA_PROVIDER_HIDDEN_POPULATED,
|
DATA_PROVIDER_HIDDEN_POPULATED,
|
||||||
DATA_PROVIDER_VISIBLE,
|
DATA_PROVIDER_VISIBLE,
|
||||||
} from './translations';
|
} from './translations';
|
||||||
import { TIMELINE_TOUR_CONFIG_ANCHORS } from '../tour/step_config';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
dataProviders: DataProvider[];
|
dataProviders: DataProvider[];
|
||||||
|
@ -112,7 +111,7 @@ export const SearchOrFilter = React.memo<Props>(
|
||||||
alignItems="flexStart"
|
alignItems="flexStart"
|
||||||
responsive={false}
|
responsive={false}
|
||||||
>
|
>
|
||||||
<EuiFlexItem grow={false} id={TIMELINE_TOUR_CONFIG_ANCHORS.DATA_VIEW}>
|
<EuiFlexItem grow={false}>
|
||||||
<Sourcerer scope={SourcererScopeName.timeline} />
|
<Sourcerer scope={SourcererScopeName.timeline} />
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
<EuiFlexItem data-test-subj="timeline-search-or-filter-search-container" grow={1}>
|
<EuiFlexItem data-test-subj="timeline-search-or-filter-search-container" grow={1}>
|
||||||
|
@ -143,7 +142,6 @@ export const SearchOrFilter = React.memo<Props>(
|
||||||
<EuiFlexItem grow={false}>
|
<EuiFlexItem grow={false}>
|
||||||
<EuiToolTip content={dataProviderIconTooltipContent}>
|
<EuiToolTip content={dataProviderIconTooltipContent}>
|
||||||
<EuiButtonIcon
|
<EuiButtonIcon
|
||||||
id={TIMELINE_TOUR_CONFIG_ANCHORS.DATA_PROVIDER}
|
|
||||||
color={buttonColor}
|
color={buttonColor}
|
||||||
isSelected={isDataProviderVisible}
|
isSelected={isDataProviderVisible}
|
||||||
iconType="timeline"
|
iconType="timeline"
|
||||||
|
|
|
@ -1,129 +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 React from 'react';
|
|
||||||
import type { TimelineTourProps } from '.';
|
|
||||||
import { TimelineTour } from '.';
|
|
||||||
import { TIMELINE_TOUR_CONFIG_ANCHORS } from './step_config';
|
|
||||||
import { useIsElementMounted } from '../../../../detection_engine/rule_management_ui/components/rules_table/rules_table/guided_onboarding/use_is_element_mounted';
|
|
||||||
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
|
|
||||||
import {
|
|
||||||
createMockStore,
|
|
||||||
createSecuritySolutionStorageMock,
|
|
||||||
TestProviders,
|
|
||||||
} from '../../../../common/mock';
|
|
||||||
import { TimelineTabs } from '../../../../../common/types';
|
|
||||||
import { TimelineTypeEnum } from '../../../../../common/api/timeline';
|
|
||||||
import { useKibana as mockUseKibana } from '../../../../common/lib/kibana/__mocks__';
|
|
||||||
import { useKibana } from '../../../../common/lib/kibana';
|
|
||||||
|
|
||||||
jest.mock(
|
|
||||||
'../../../../detection_engine/rule_management_ui/components/rules_table/rules_table/guided_onboarding/use_is_element_mounted'
|
|
||||||
);
|
|
||||||
jest.mock('../../../../common/lib/kibana');
|
|
||||||
|
|
||||||
const mockedUseKibana = mockUseKibana();
|
|
||||||
|
|
||||||
const switchTabMock = jest.fn();
|
|
||||||
const { storage: storageMock } = createSecuritySolutionStorageMock();
|
|
||||||
const mockStore = createMockStore(undefined, undefined, undefined, storageMock);
|
|
||||||
|
|
||||||
const TestComponent = (props: Partial<TimelineTourProps> = {}) => {
|
|
||||||
return (
|
|
||||||
<TestProviders store={mockStore}>
|
|
||||||
<TimelineTour
|
|
||||||
activeTab={TimelineTabs.query}
|
|
||||||
switchToTab={switchTabMock}
|
|
||||||
timelineType={TimelineTypeEnum.default}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
{Object.values(TIMELINE_TOUR_CONFIG_ANCHORS).map((anchor) => {
|
|
||||||
return <div id={anchor} key={anchor} />;
|
|
||||||
})}
|
|
||||||
</TestProviders>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('Timeline Tour', () => {
|
|
||||||
beforeAll(() => {
|
|
||||||
(useIsElementMounted as jest.Mock).mockReturnValue(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
(useKibana as jest.Mock).mockReturnValue({
|
|
||||||
...mockedUseKibana,
|
|
||||||
services: {
|
|
||||||
...mockedUseKibana.services,
|
|
||||||
storage: storageMock,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
storageMock.clear();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not render tour steps when element are not mounted', () => {
|
|
||||||
(useIsElementMounted as jest.Mock).mockReturnValueOnce(false);
|
|
||||||
render(<TestComponent />);
|
|
||||||
expect(screen.queryByTestId('timeline-tour-step-1')).toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render tour steps when element are mounted', async () => {
|
|
||||||
render(<TestComponent />);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(screen.getByTestId('timeline-tour-step-1')).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
fireEvent.click(screen.getByText('Next'));
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(screen.getByTestId('timeline-tour-step-2')).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
fireEvent.click(screen.getByText('Next'));
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(screen.getByTestId('timeline-tour-step-3')).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
fireEvent.click(screen.getByText('Next'));
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(screen.getByTestId('timeline-tour-step-4')).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
fireEvent.click(screen.getByText('Next'));
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(screen.queryByText('Finish tour')).toBeVisible();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render different tour steps when timeline type is template', async () => {
|
|
||||||
render(<TestComponent timelineType={TimelineTypeEnum.template} />);
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(screen.getByTestId('timeline-tour-step-1')).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
fireEvent.click(screen.getByText('Next'));
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(screen.getByTestId('timeline-tour-step-2')).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
fireEvent.click(screen.getByText('Next'));
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(screen.getByTestId('timeline-tour-step-3')).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
fireEvent.click(screen.getByText('Next'));
|
|
||||||
|
|
||||||
await waitFor(() => {
|
|
||||||
expect(screen.queryByText('Finish tour')).toBeVisible();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,142 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This timeline tour only valid for 8.12 release is not needed for 8.13
|
|
||||||
*
|
|
||||||
* */
|
|
||||||
|
|
||||||
import React, { useEffect, useCallback, useState, useMemo } from 'react';
|
|
||||||
import { EuiButton, EuiButtonEmpty, EuiTourStep } from '@elastic/eui';
|
|
||||||
import type { TimelineType } from '../../../../../common/api/timeline';
|
|
||||||
import type { TimelineTabs } from '../../../../../common/types';
|
|
||||||
import { useIsElementMounted } from '../../../../detection_engine/rule_management_ui/components/rules_table/rules_table/guided_onboarding/use_is_element_mounted';
|
|
||||||
import { NEW_FEATURES_TOUR_STORAGE_KEYS } from '../../../../../common/constants';
|
|
||||||
import { useKibana } from '../../../../common/lib/kibana';
|
|
||||||
import { timelineTourSteps, tourConfig } from './step_config';
|
|
||||||
import * as i18n from './translations';
|
|
||||||
|
|
||||||
interface TourState {
|
|
||||||
currentTourStep: number;
|
|
||||||
isTourActive: boolean;
|
|
||||||
tourPopoverWidth: number;
|
|
||||||
tourSubtitle: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TimelineTourProps {
|
|
||||||
activeTab: TimelineTabs;
|
|
||||||
timelineType: TimelineType;
|
|
||||||
switchToTab: (tab: TimelineTabs) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TimelineTourComp = (props: TimelineTourProps) => {
|
|
||||||
const { activeTab, switchToTab, timelineType } = props;
|
|
||||||
const {
|
|
||||||
services: { storage },
|
|
||||||
} = useKibana();
|
|
||||||
|
|
||||||
const updatedTourSteps = useMemo(
|
|
||||||
() =>
|
|
||||||
timelineTourSteps.filter((step) => !step.timelineType || step.timelineType === timelineType),
|
|
||||||
[timelineType]
|
|
||||||
);
|
|
||||||
|
|
||||||
const [tourState, setTourState] = useState<TourState>(() => {
|
|
||||||
const restoredTourState = storage.get(NEW_FEATURES_TOUR_STORAGE_KEYS.TIMELINE);
|
|
||||||
if (restoredTourState != null) {
|
|
||||||
return restoredTourState;
|
|
||||||
}
|
|
||||||
return tourConfig;
|
|
||||||
});
|
|
||||||
|
|
||||||
const finishTour = useCallback(() => {
|
|
||||||
setTourState((prev) => {
|
|
||||||
return {
|
|
||||||
...prev,
|
|
||||||
isTourActive: false,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const nextStep = useCallback(() => {
|
|
||||||
setTourState((prev) => {
|
|
||||||
return {
|
|
||||||
...prev,
|
|
||||||
currentTourStep: prev.currentTourStep + 1,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
storage.set(NEW_FEATURES_TOUR_STORAGE_KEYS.TIMELINE, tourState);
|
|
||||||
}, [tourState, storage]);
|
|
||||||
|
|
||||||
const getFooterAction = useCallback(
|
|
||||||
(step: number) => {
|
|
||||||
// if it's the last step, we don't want to show the next button
|
|
||||||
return step === updatedTourSteps.length ? (
|
|
||||||
<EuiButton color="success" size="s" onClick={finishTour}>
|
|
||||||
{i18n.TIMELINE_TOUR_FINISH}
|
|
||||||
</EuiButton>
|
|
||||||
) : (
|
|
||||||
[
|
|
||||||
<EuiButtonEmpty size="s" color="text" onClick={finishTour}>
|
|
||||||
{i18n.TIMELINE_TOUR_EXIT}
|
|
||||||
</EuiButtonEmpty>,
|
|
||||||
<EuiButton color="success" size="s" onClick={nextStep}>
|
|
||||||
{i18n.TIMELINE_TOUR_NEXT}
|
|
||||||
</EuiButton>,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[finishTour, nextStep, updatedTourSteps.length]
|
|
||||||
);
|
|
||||||
|
|
||||||
const nextEl = updatedTourSteps[tourState.currentTourStep - 1]?.anchor;
|
|
||||||
|
|
||||||
const isElementAtCurrentStepMounted = useIsElementMounted(nextEl);
|
|
||||||
|
|
||||||
const currentStepConfig = updatedTourSteps[tourState.currentTourStep - 1];
|
|
||||||
|
|
||||||
if (currentStepConfig?.timelineTab && currentStepConfig.timelineTab !== activeTab) {
|
|
||||||
switchToTab(currentStepConfig.timelineTab);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tourState.isTourActive || !isElementAtCurrentStepMounted) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{updatedTourSteps.map((steps, idx) => {
|
|
||||||
const stepCount = idx + 1;
|
|
||||||
if (tourState.currentTourStep !== stepCount) return null;
|
|
||||||
const panelProps = {
|
|
||||||
'data-test-subj': `timeline-tour-step-${idx + 1}`,
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<EuiTourStep
|
|
||||||
panelProps={panelProps}
|
|
||||||
key={idx}
|
|
||||||
step={stepCount}
|
|
||||||
isStepOpen={tourState.isTourActive && tourState.currentTourStep === idx + 1}
|
|
||||||
minWidth={tourState.tourPopoverWidth}
|
|
||||||
stepsTotal={updatedTourSteps.length}
|
|
||||||
onFinish={finishTour}
|
|
||||||
title={steps.title}
|
|
||||||
content={steps.content}
|
|
||||||
anchor={`#${steps.anchor}`}
|
|
||||||
subtitle={tourConfig.tourSubtitle}
|
|
||||||
footerAction={getFooterAction(stepCount)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const TimelineTour = React.memo(TimelineTourComp);
|
|
|
@ -1,98 +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 { EuiText, EuiCode } from '@elastic/eui';
|
|
||||||
import React from 'react';
|
|
||||||
import { FormattedMessage } from '@kbn/i18n-react';
|
|
||||||
import { TimelineTypeEnum } from '../../../../../common/api/timeline';
|
|
||||||
import { TimelineTabs } from '../../../../../common/types';
|
|
||||||
import * as i18n from './translations';
|
|
||||||
|
|
||||||
export const TIMELINE_TOUR_CONFIG_ANCHORS = {
|
|
||||||
ACTION_MENU: 'timeline-action-menu',
|
|
||||||
DATA_VIEW: 'timeline-data-view',
|
|
||||||
DATA_PROVIDER: 'toggle-data-provider',
|
|
||||||
SAVE_TIMELINE: 'save-timeline-action',
|
|
||||||
ADD_TO_FAVORITES: 'add-to-favorites',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const timelineTourSteps = [
|
|
||||||
{
|
|
||||||
title: i18n.TIMELINE_TOUR_TIMELINE_ACTIONS_STEP_TITLE,
|
|
||||||
content: (
|
|
||||||
<EuiText>
|
|
||||||
<FormattedMessage
|
|
||||||
id="xpack.securitySolution.timeline.tour.newTimeline.description"
|
|
||||||
defaultMessage="Click {newButton} to create a new Timeline. Click {openButton} to open an existing one."
|
|
||||||
values={{
|
|
||||||
newButton: <EuiCode>{i18n.TIMELINE_TOUR_NEW}</EuiCode>,
|
|
||||||
openButton: <EuiCode>{i18n.TIMELINE_TOUR_OPEN}</EuiCode>,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</EuiText>
|
|
||||||
),
|
|
||||||
anchor: TIMELINE_TOUR_CONFIG_ANCHORS.ACTION_MENU,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: i18n.TIMELINE_TOUR_ADD_TO_FAVORITES_STEP_TITLE,
|
|
||||||
content: (
|
|
||||||
<EuiText>
|
|
||||||
<FormattedMessage
|
|
||||||
id="xpack.securitySolution.timeline.tour.addToFavorites.description"
|
|
||||||
defaultMessage="Click to favorite your Timeline so you can quickly find it later."
|
|
||||||
/>
|
|
||||||
</EuiText>
|
|
||||||
),
|
|
||||||
anchor: TIMELINE_TOUR_CONFIG_ANCHORS.ADD_TO_FAVORITES,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
timelineTab: TimelineTabs.query,
|
|
||||||
title: i18n.TIMELINE_TOUR_CHANGE_DATA_VIEW_TITLE,
|
|
||||||
content: (
|
|
||||||
<EuiText>
|
|
||||||
<FormattedMessage
|
|
||||||
id="xpack.securitySolution.timeline.tour.changeDataView.description"
|
|
||||||
defaultMessage="Click the {dataViewButton} menu to choose the event or alert data that you want to display."
|
|
||||||
values={{
|
|
||||||
dataViewButton: <EuiCode> {i18n.TIMELINE_TOUR_DATA_VIEW}</EuiCode>,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</EuiText>
|
|
||||||
),
|
|
||||||
anchor: TIMELINE_TOUR_CONFIG_ANCHORS.DATA_VIEW,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
timelineType: TimelineTypeEnum.default,
|
|
||||||
timelineTab: TimelineTabs.query,
|
|
||||||
title: i18n.TIMELINE_TOUR_DATA_PROVIDER_VISIBILITY_TITLE,
|
|
||||||
content: <EuiText>{i18n.TIMELINE_TOUR_DATA_PROVIDER_VISIBILITY_DESCRIPTION}</EuiText>,
|
|
||||||
anchor: TIMELINE_TOUR_CONFIG_ANCHORS.DATA_PROVIDER,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: i18n.TIMELINE_TOUR_SAVE_TIMELINE_STEP_TITLE,
|
|
||||||
content: (
|
|
||||||
<EuiText>
|
|
||||||
<FormattedMessage
|
|
||||||
id="xpack.securitySolution.timeline.tour.saveTimeline.description"
|
|
||||||
defaultMessage="Click {saveButton} to manually save new changes. While saving your Timeline, you can {editButton} its name and description or save it as a new Timeline."
|
|
||||||
values={{
|
|
||||||
saveButton: <EuiCode>{i18n.TIMELINE_TOUR_SAVE}</EuiCode>,
|
|
||||||
editButton: <EuiCode>{i18n.TIMELINE_TOUR_EDIT}</EuiCode>,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</EuiText>
|
|
||||||
),
|
|
||||||
anchor: TIMELINE_TOUR_CONFIG_ANCHORS.SAVE_TIMELINE,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const tourConfig = {
|
|
||||||
currentTourStep: 1,
|
|
||||||
isTourActive: true,
|
|
||||||
tourPopoverWidth: 300,
|
|
||||||
tourSubtitle: i18n.TIMELINE_TOUR_SUBTITLE,
|
|
||||||
};
|
|
|
@ -1,92 +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 TIMELINE_TOUR_SUBTITLE = i18n.translate(
|
|
||||||
'xpack.securitySolution.timeline.tour.subTitle',
|
|
||||||
{
|
|
||||||
defaultMessage: 'Recent Timeline improvements',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const TIMELINE_TOUR_TIMELINE_ACTIONS_STEP_TITLE = i18n.translate(
|
|
||||||
'xpack.securitySolution.timeline.tour.newTimeline.title',
|
|
||||||
{
|
|
||||||
defaultMessage: 'Actions are now easier to find',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const TIMELINE_TOUR_DATA_PROVIDER_VISIBILITY_TITLE = i18n.translate(
|
|
||||||
'xpack.securitySolution.timeline.tour.dataProviderToggle.title',
|
|
||||||
{
|
|
||||||
defaultMessage: 'The query builder is collapsed by default',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const TIMELINE_TOUR_DATA_PROVIDER_VISIBILITY_DESCRIPTION = i18n.translate(
|
|
||||||
'xpack.securitySolution.timeline.tour.dataProviderToggle.description',
|
|
||||||
{
|
|
||||||
defaultMessage: 'Click to expand or collapse the query builder.',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const TIMELINE_TOUR_SAVE_TIMELINE_STEP_TITLE = i18n.translate(
|
|
||||||
'xpack.securitySolution.timeline.tour.saveTimeline.title',
|
|
||||||
{
|
|
||||||
defaultMessage: 'An easier way to save new changes',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const TIMELINE_TOUR_CHANGE_DATA_VIEW_TITLE = i18n.translate(
|
|
||||||
'xpack.securitySolution.timeline.tour.changeDataView.title',
|
|
||||||
{
|
|
||||||
defaultMessage: 'The Data view menu has moved',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const TIMELINE_TOUR_ADD_TO_FAVORITES_STEP_TITLE = i18n.translate(
|
|
||||||
'xpack.securitySolution.timeline.tour.addToFavorites.title',
|
|
||||||
{
|
|
||||||
defaultMessage: 'A new and intuitive way to favorite your Timeline',
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const TIMELINE_TOUR_NEXT = i18n.translate('xpack.securitySolution.timeline.tour.next', {
|
|
||||||
defaultMessage: 'Next',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const TIMELINE_TOUR_FINISH = i18n.translate('xpack.securitySolution.timeline.tour.finish', {
|
|
||||||
defaultMessage: 'Finish tour',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const TIMELINE_TOUR_EXIT = i18n.translate('xpack.securitySolution.timeline.tour.exit', {
|
|
||||||
defaultMessage: 'Exit tour',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const TIMELINE_TOUR_SAVE = i18n.translate('xpack.securitySolution.timeline.tour.save', {
|
|
||||||
defaultMessage: 'Save',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const TIMELINE_TOUR_NEW = i18n.translate('xpack.securitySolution.timeline.tour.new', {
|
|
||||||
defaultMessage: 'New',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const TIMELINE_TOUR_OPEN = i18n.translate('xpack.securitySolution.timeline.tour.open', {
|
|
||||||
defaultMessage: 'Open',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const TIMELINE_TOUR_EDIT = i18n.translate('xpack.securitySolution.timeline.tour.edit', {
|
|
||||||
defaultMessage: 'Edit',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const TIMELINE_TOUR_DATA_VIEW = i18n.translate(
|
|
||||||
'xpack.securitySolution.timeline.tour.dataView',
|
|
||||||
{
|
|
||||||
defaultMessage: 'Data view',
|
|
||||||
}
|
|
||||||
);
|
|
|
@ -40443,23 +40443,6 @@
|
||||||
"xpack.securitySolution.timeline.tabs.sessionTabTimelineTitle": "Vue de session",
|
"xpack.securitySolution.timeline.tabs.sessionTabTimelineTitle": "Vue de session",
|
||||||
"xpack.securitySolution.timeline.tcp": "TCP",
|
"xpack.securitySolution.timeline.tcp": "TCP",
|
||||||
"xpack.securitySolution.timeline.toggleEventDetailsTitle": "Développer les détails de l'événement",
|
"xpack.securitySolution.timeline.toggleEventDetailsTitle": "Développer les détails de l'événement",
|
||||||
"xpack.securitySolution.timeline.tour.addToFavorites.description": "Cliquez pour ajouter votre chronologie à vos favoris afin de la retrouver plus rapidement par la suite.",
|
|
||||||
"xpack.securitySolution.timeline.tour.addToFavorites.title": "Une nouvelle façon intuitive d'ajouter votre chronologie à vos favoris",
|
|
||||||
"xpack.securitySolution.timeline.tour.changeDataView.description": "Cliquez sur le menu {dataViewButton} pour choisir les données d'événement ou d'alerte à afficher.",
|
|
||||||
"xpack.securitySolution.timeline.tour.changeDataView.title": "Le menu d'affichage des données s'est déplacé",
|
|
||||||
"xpack.securitySolution.timeline.tour.dataProviderToggle.description": "Cliquez pour développer ou réduire le générateur de requêtes.",
|
|
||||||
"xpack.securitySolution.timeline.tour.dataProviderToggle.title": "Le constructeur de requêtes est réduit par défaut",
|
|
||||||
"xpack.securitySolution.timeline.tour.dataView": "Vue de données",
|
|
||||||
"xpack.securitySolution.timeline.tour.edit": "Modifier",
|
|
||||||
"xpack.securitySolution.timeline.tour.exit": "Sortir du tour",
|
|
||||||
"xpack.securitySolution.timeline.tour.finish": "Finir le tour",
|
|
||||||
"xpack.securitySolution.timeline.tour.new": "Nouveauté",
|
|
||||||
"xpack.securitySolution.timeline.tour.newTimeline.title": "Les actions sont désormais plus faciles à trouver",
|
|
||||||
"xpack.securitySolution.timeline.tour.next": "Suivant",
|
|
||||||
"xpack.securitySolution.timeline.tour.open": "Ouvrir",
|
|
||||||
"xpack.securitySolution.timeline.tour.save": "Enregistrer",
|
|
||||||
"xpack.securitySolution.timeline.tour.saveTimeline.title": "Une façon plus simple d'enregistrer les nouveaux changements",
|
|
||||||
"xpack.securitySolution.timeline.tour.subTitle": "Améliorations de la chronologie récente",
|
|
||||||
"xpack.securitySolution.timeline.typeTooltip": "Type",
|
"xpack.securitySolution.timeline.typeTooltip": "Type",
|
||||||
"xpack.securitySolution.timeline.unsavedWorkMessage": "Quitter Timeline avec un travail non enregistré ?",
|
"xpack.securitySolution.timeline.unsavedWorkMessage": "Quitter Timeline avec un travail non enregistré ?",
|
||||||
"xpack.securitySolution.timeline.unsavedWorkTitle": "Modifications non enregistrées",
|
"xpack.securitySolution.timeline.unsavedWorkTitle": "Modifications non enregistrées",
|
||||||
|
|
|
@ -40187,25 +40187,6 @@
|
||||||
"xpack.securitySolution.timeline.tabs.sessionTabTimelineTitle": "セッションビュー",
|
"xpack.securitySolution.timeline.tabs.sessionTabTimelineTitle": "セッションビュー",
|
||||||
"xpack.securitySolution.timeline.tcp": "TCP",
|
"xpack.securitySolution.timeline.tcp": "TCP",
|
||||||
"xpack.securitySolution.timeline.toggleEventDetailsTitle": "イベントの詳細を展開",
|
"xpack.securitySolution.timeline.toggleEventDetailsTitle": "イベントの詳細を展開",
|
||||||
"xpack.securitySolution.timeline.tour.addToFavorites.description": "クリックすると、タイムラインがお気に入りに追加され、後ですばやく見つけられるようになります。",
|
|
||||||
"xpack.securitySolution.timeline.tour.addToFavorites.title": "タイムラインをお気に入りに追加するための新しい直感的な方法",
|
|
||||||
"xpack.securitySolution.timeline.tour.changeDataView.description": "{dataViewButton}メニューをクリックして、表示するイベントまたはアラートデータを選択します。",
|
|
||||||
"xpack.securitySolution.timeline.tour.changeDataView.title": "データビューメニューが移動しました",
|
|
||||||
"xpack.securitySolution.timeline.tour.dataProviderToggle.description": "クリックすると、クエリビルダーが展開されたり、折りたたまれたりします。",
|
|
||||||
"xpack.securitySolution.timeline.tour.dataProviderToggle.title": "クエリビルダーはデフォルトで折りたたまれています",
|
|
||||||
"xpack.securitySolution.timeline.tour.dataView": "データビュー",
|
|
||||||
"xpack.securitySolution.timeline.tour.edit": "編集",
|
|
||||||
"xpack.securitySolution.timeline.tour.exit": "ガイドを終了",
|
|
||||||
"xpack.securitySolution.timeline.tour.finish": "ガイドを完了",
|
|
||||||
"xpack.securitySolution.timeline.tour.new": "新規",
|
|
||||||
"xpack.securitySolution.timeline.tour.newTimeline.description": "{newButton}をクリックして、新しいタイムラインを作成します。{openButton}をクリックして、既存の項目を開きます。",
|
|
||||||
"xpack.securitySolution.timeline.tour.newTimeline.title": "アクションが見つけやすくなりました",
|
|
||||||
"xpack.securitySolution.timeline.tour.next": "次へ",
|
|
||||||
"xpack.securitySolution.timeline.tour.open": "開く",
|
|
||||||
"xpack.securitySolution.timeline.tour.save": "保存",
|
|
||||||
"xpack.securitySolution.timeline.tour.saveTimeline.description": "{saveButton}をクリックして、新しい変更を手動で保存します。タイムラインを保存するときには、名前と説明を{editButton}するか、新しいタイムラインとして保存することができます。",
|
|
||||||
"xpack.securitySolution.timeline.tour.saveTimeline.title": "新しい変更を保存する方法が簡単になりました",
|
|
||||||
"xpack.securitySolution.timeline.tour.subTitle": "最近のタイムラインの改良",
|
|
||||||
"xpack.securitySolution.timeline.typeTooltip": "型",
|
"xpack.securitySolution.timeline.typeTooltip": "型",
|
||||||
"xpack.securitySolution.timeline.unsavedWorkMessage": "作業を保存せずにタイムラインから移動しますか?",
|
"xpack.securitySolution.timeline.unsavedWorkMessage": "作業を保存せずにタイムラインから移動しますか?",
|
||||||
"xpack.securitySolution.timeline.unsavedWorkTitle": "保存されていない変更",
|
"xpack.securitySolution.timeline.unsavedWorkTitle": "保存されていない変更",
|
||||||
|
|
|
@ -40232,25 +40232,6 @@
|
||||||
"xpack.securitySolution.timeline.tabs.sessionTabTimelineTitle": "会话视图",
|
"xpack.securitySolution.timeline.tabs.sessionTabTimelineTitle": "会话视图",
|
||||||
"xpack.securitySolution.timeline.tcp": "TCP",
|
"xpack.securitySolution.timeline.tcp": "TCP",
|
||||||
"xpack.securitySolution.timeline.toggleEventDetailsTitle": "展开事件详情",
|
"xpack.securitySolution.timeline.toggleEventDetailsTitle": "展开事件详情",
|
||||||
"xpack.securitySolution.timeline.tour.addToFavorites.description": "单击以收藏您的时间线,以便稍后快速找到它。",
|
|
||||||
"xpack.securitySolution.timeline.tour.addToFavorites.title": "收藏时间线的全新直观方式",
|
|
||||||
"xpack.securitySolution.timeline.tour.changeDataView.description": "单击 {dataViewButton} 菜单以选择要显示的事件或告警数据。",
|
|
||||||
"xpack.securitySolution.timeline.tour.changeDataView.title": "已移动数据视图菜单",
|
|
||||||
"xpack.securitySolution.timeline.tour.dataProviderToggle.description": "单击以展开或折叠查询构建器。",
|
|
||||||
"xpack.securitySolution.timeline.tour.dataProviderToggle.title": "查询构建器默认处于折叠状态",
|
|
||||||
"xpack.securitySolution.timeline.tour.dataView": "数据视图",
|
|
||||||
"xpack.securitySolution.timeline.tour.edit": "编辑",
|
|
||||||
"xpack.securitySolution.timeline.tour.exit": "退出教程",
|
|
||||||
"xpack.securitySolution.timeline.tour.finish": "完成教程",
|
|
||||||
"xpack.securitySolution.timeline.tour.new": "新建",
|
|
||||||
"xpack.securitySolution.timeline.tour.newTimeline.description": "单击 {newButton} 以创建新时间线。单击 {openButton} 以打开现有项。",
|
|
||||||
"xpack.securitySolution.timeline.tour.newTimeline.title": "现在可以更轻松地查找操作",
|
|
||||||
"xpack.securitySolution.timeline.tour.next": "下一步",
|
|
||||||
"xpack.securitySolution.timeline.tour.open": "打开",
|
|
||||||
"xpack.securitySolution.timeline.tour.save": "保存",
|
|
||||||
"xpack.securitySolution.timeline.tour.saveTimeline.description": "单击 {saveButton} 以手动保存新更改。在保存时间线时,您可以{editButton}其名称和描述,或将其另存为新时间线。",
|
|
||||||
"xpack.securitySolution.timeline.tour.saveTimeline.title": "保存新更改的更简便方法",
|
|
||||||
"xpack.securitySolution.timeline.tour.subTitle": "最近的时间线改进",
|
|
||||||
"xpack.securitySolution.timeline.typeTooltip": "类型",
|
"xpack.securitySolution.timeline.typeTooltip": "类型",
|
||||||
"xpack.securitySolution.timeline.unsavedWorkMessage": "离开有未保存工作的时间线?",
|
"xpack.securitySolution.timeline.unsavedWorkMessage": "离开有未保存工作的时间线?",
|
||||||
"xpack.securitySolution.timeline.unsavedWorkTitle": "未保存的更改",
|
"xpack.securitySolution.timeline.unsavedWorkTitle": "未保存的更改",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue