From 1ba247ee719565ce3dd345ea0138a60bd71a6656 Mon Sep 17 00:00:00 2001 From: Jatin Kathuria Date: Thu, 14 Dec 2023 00:17:19 +0100 Subject: [PATCH] [Security Solution] Timeline UI refactor revision - Design feedback (#173015) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary This PR implements Design feedback for the Timeline UI refactoring: 1. Adds ⨁ back in timeline bottom bar. ![image](https://github.com/elastic/kibana/assets/7485038/1572527a-471b-4edb-826a-29610eae0a4a) 2. Add `Unsaved` badge for better visibility ![image](https://github.com/elastic/kibana/assets/7485038/b1baf745-fcda-4346-a104-a78b88853ce4) ===> ![image](https://github.com/elastic/kibana/assets/7485038/9d279276-cc86-44d4-903c-53ebe60e55fb) 3. Adds a new tour step for `Add to Favorites` Icon. https://github.com/elastic/kibana/assets/7485038/2d4f3e2e-7868-4fbb-8a73-c9b427d86e04 ### 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] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed --- .../components/super_date_picker/index.tsx | 6 ++ .../flyout/__snapshots__/index.test.tsx.snap | 82 ++++++++++++++----- .../components/flyout/action_menu/index.tsx | 22 +++-- .../flyout/add_timeline_button/index.test.tsx | 4 +- .../flyout/add_timeline_button/index.tsx | 4 +- .../components/flyout/header/index.tsx | 12 ++- .../header/timeline_status_info.test.tsx | 14 +--- .../flyout/header/timeline_status_info.tsx | 21 ++--- .../components/flyout/header/translations.ts | 2 +- .../timelines/components/timeline/index.tsx | 20 ++++- .../timeline/properties/helpers.tsx | 4 + .../timeline/properties/translations.ts | 4 +- .../components/timeline/tour/index.test.tsx | 11 ++- .../components/timeline/tour/index.tsx | 29 +++++-- .../components/timeline/tour/step_config.tsx | 26 ++++-- .../components/timeline/tour/translations.ts | 9 +- .../timeline_templates/creation.cy.ts | 7 ++ .../investigations/timelines/creation.cy.ts | 7 +- .../cypress/screens/timeline.ts | 2 +- .../cypress/tasks/timeline.ts | 24 ++++++ 20 files changed, 222 insertions(+), 88 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.tsx b/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.tsx index 56f81529600d..b79f9c736f5c 100644 --- a/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.tsx @@ -9,6 +9,7 @@ import dateMath from '@kbn/datemath'; import type { EuiSuperDatePickerProps, EuiSuperDatePickerRecentRange, + EuiSuperUpdateButtonProps, OnRefreshChangeProps, OnRefreshProps, OnTimeChangeProps, @@ -42,6 +43,10 @@ import { } from './selectors'; import type { Inputs } from '../../store/inputs/model'; +const refreshButtonProps: EuiSuperUpdateButtonProps = { + fill: false, +}; + const MAX_RECENTLY_USED_RANGES = 9; interface Range { @@ -219,6 +224,7 @@ export const SuperDatePickerComponent = React.memo( isDisabled={disabled} width={width} compressed={compressed} + updateButtonProps={refreshButtonProps} /> ); }, diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/flyout/__snapshots__/index.test.tsx.snap index 88bf41423561..5ef25f98932c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/__snapshots__/index.test.tsx.snap @@ -62,6 +62,56 @@ exports[`Flyout rendering it renders correctly against snapshot 1`] = `
+
+
+
+
+ +
+
+
+
+
+ +
@@ -109,31 +159,21 @@ exports[`Flyout rendering it renders correctly against snapshot 1`] = ` data-test-subj="timeline-status" > - Unsaved + + + Unsaved + +
-
- -
diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/index.tsx index b2e3230490ce..2208b6ee706b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/index.tsx @@ -7,6 +7,7 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React from 'react'; +import styled from 'styled-components'; import { useKibana } from '../../../../common/lib/kibana/kibana_react'; import { APP_ID } from '../../../../../common'; import type { TimelineTabs } from '../../../../../common/types'; @@ -25,6 +26,12 @@ interface TimelineActionMenuProps { activeTab: TimelineTabs; } +const VerticalDivider = styled.span` + width: 0px; + height: 20px; + border-left: 1px solid ${({ theme }) => theme.eui.euiColorLightShade}; +`; + const TimelineActionMenuComponent = ({ mode = 'normal', timelineId, @@ -49,11 +56,6 @@ const TimelineActionMenuComponent = ({ - {userCasesPermissions.create && userCasesPermissions.read ? ( - - - - ) : null} + {userCasesPermissions.create && userCasesPermissions.read ? ( + <> + + + + + + + + ) : null} diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.test.tsx index 80bd77bb8109..efae77571138 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.test.tsx @@ -52,11 +52,11 @@ jest.mock('../../../containers/all', () => { }); jest.mock('../../timeline/properties/new_template_timeline', () => ({ - NewTemplateTimeline: jest.fn(() =>
{'Create new timeline template'}
), + NewTemplateTimeline: jest.fn(() =>
{'Create new Timeline template'}
), })); jest.mock('../../timeline/properties/helpers', () => ({ - NewTimeline: jest.fn().mockReturnValue(
{'Create new timeline'}
), + NewTimeline: jest.fn().mockReturnValue(
{'Create new Timeline'}
), })); jest.mock('../../../../common/containers/source', () => ({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.tsx index 69b71adb9fb6..2c9e537ce4b0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.tsx @@ -39,10 +39,10 @@ const AddTimelineButtonComponent: React.FC = ({ () => ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx index 14158e884e1b..b6e4d354e9c6 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx @@ -28,6 +28,7 @@ import { TimelineActionMenu } from '../action_menu'; import { AddToFavoritesButton } from '../../timeline/properties/helpers'; import { TimelineStatusInfo } from './timeline_status_info'; import { timelineDefaults } from '../../../store/timeline/defaults'; +import { AddTimelineButton } from '../add_timeline_button'; interface FlyoutHeaderPanelProps { timelineId: string; @@ -141,6 +142,14 @@ const FlyoutHeaderPanelComponent: React.FC = ({ timeline > + {!show ? ( + + + + ) : null} + + + = ({ timeline - - - {show && ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.test.tsx index f7fda862f793..55285e18638c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.test.tsx @@ -28,18 +28,6 @@ describe('TestComponent', () => { it('should render the status correctly when timeline has unsaved changes', () => { render(); - expect(screen.getByText('Has unsaved changes')).toBeVisible(); - }); - - it('should render the status correctly when timeline is saved', () => { - const updatedTime = Date.now(); - render(); - expect(screen.getByText('Saved')).toBeVisible(); - }); - - it('should render the status correctly when timeline is saved some time ago', () => { - const updatedTime = Date.now() - 10000; - render(); - expect(screen.getByTestId('timeline-status')).toHaveTextContent(/Saved10 seconds ago/); + expect(screen.getByText('Unsaved changes')).toBeVisible(); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.tsx index ed164ddab47f..ebd2f5bede31 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.tsx @@ -6,8 +6,7 @@ */ import React from 'react'; -import { EuiTextColor, EuiText } from '@elastic/eui'; -import { FormattedRelative } from '@kbn/i18n-react'; +import { EuiText, EuiBadge } from '@elastic/eui'; import styled from 'styled-components'; import { TimelineStatus } from '../../../../../common/api/timeline'; @@ -29,21 +28,13 @@ export const TimelineStatusInfo = React.memo( let statusContent: React.ReactNode = null; if (isUnsaved || !updated) { - statusContent = {i18n.UNSAVED}; + statusContent = {i18n.UNSAVED}; } else if (changed) { - statusContent = {i18n.UNSAVED_CHANGES}; - } else { - statusContent = ( - <> - {i18n.SAVED} - - - ); + statusContent = {i18n.UNSAVED_CHANGES}; } + + if (!statusContent) return null; + return ( {statusContent} diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/translations.ts b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/translations.ts index 56036f899e61..f3e7306eae31 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/translations.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/translations.ts @@ -26,7 +26,7 @@ export const SAVED = i18n.translate('xpack.securitySolution.timeline.properties. export const UNSAVED_CHANGES = i18n.translate( 'xpack.securitySolution.timeline.properties.hasChangesLabel', { - defaultMessage: 'Has unsaved changes', + defaultMessage: 'Unsaved changes', } ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx index 5ec2a07af5bb..bffc5c45f84e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx @@ -19,7 +19,7 @@ import { defaultHeaders } from './body/column_headers/default_headers'; import type { CellValueElementProps } from './cell_rendering'; import { SourcererScopeName } from '../../../common/store/sourcerer/model'; import { FlyoutHeaderPanel } from '../flyout/header'; -import type { TimelineId, RowRenderer } from '../../../../common/types/timeline'; +import type { TimelineId, RowRenderer, TimelineTabs } from '../../../../common/types/timeline'; import { TimelineType } from '../../../../common/api/timeline'; import { useDeepEqualSelector, useShallowEqualSelector } from '../../../common/hooks/use_selector'; import { activeTimeline } from '../../containers/active_timeline_context'; @@ -82,6 +82,7 @@ const StatefulTimelineComponent: React.FC = ({ initialized, show: isOpen, isLoading, + activeTab, } = useDeepEqualSelector((state) => pick( [ @@ -95,6 +96,7 @@ const StatefulTimelineComponent: React.FC = ({ 'initialized', 'show', 'isLoading', + 'activeTab', ], getTimeline(state, timelineId) ?? timelineDefaults ) @@ -195,6 +197,18 @@ const StatefulTimelineComponent: React.FC = ({ const showTimelineTour = isOpen && !isLoading && canEditTimeline; + const handleSwitchToTab = useCallback( + (tab: TimelineTabs) => { + dispatch( + timelineActions.setActiveTabTimeline({ + id: timelineId, + activeTab: tab, + }) + ); + }, + [timelineId, dispatch] + ); + return ( = ({ /> - {showTimelineTour ? : null} + {showTimelineTour ? ( + + ) : null} ); }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx index 0330e3ed74d5..b8c0366e3f39 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx @@ -18,6 +18,7 @@ import { useShallowEqualSelector } from '../../../../common/hooks/use_selector'; import * as i18n from './translations'; import { useCreateTimelineButton } from './use_create_timeline'; import { timelineDefaults } from '../../../store/timeline/defaults'; +import { TIMELINE_TOUR_CONFIG_ANCHORS } from '../tour/step_config'; const NotesCountBadge = styled(EuiBadge)` margin-left: 5px; @@ -56,7 +57,9 @@ const AddToFavoritesButtonComponent: React.FC = ({ return compact ? ( = ({ /> ) : ( { return ( - + {Object.values(TIMELINE_TOUR_CONFIG_ANCHORS).map((anchor) => { return
; })} @@ -58,6 +61,12 @@ describe('Timeline Tour', () => { 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(); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/index.tsx index 96c703fb1b84..728579868846 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/index.tsx @@ -12,6 +12,7 @@ import React, { useEffect, useCallback, useState } from 'react'; import { EuiButton, EuiButtonEmpty, EuiTourStep } from '@elastic/eui'; +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'; @@ -25,7 +26,13 @@ interface TourState { tourSubtitle: string; } -const TimelineTourComp = () => { +interface TimelineTourProps { + activeTab: TimelineTabs; + switchToTab: (tab: TimelineTabs) => void; +} + +const TimelineTourComp = (props: TimelineTourProps) => { + const { activeTab, switchToTab } = props; const { services: { storage }, } = useKibana(); @@ -86,6 +93,12 @@ const TimelineTourComp = () => { const isElementAtCurrentStepMounted = useIsElementMounted(nextEl); + const currentStepConfig = timelineTourSteps[tourState.currentTourStep - 1]; + + if (currentStepConfig?.timelineTab && currentStepConfig.timelineTab !== activeTab) { + switchToTab(currentStepConfig.timelineTab); + } + if (!tourState.isTourActive || !isElementAtCurrentStepMounted) { return null; } @@ -93,14 +106,16 @@ const TimelineTourComp = () => { return ( <> {timelineTourSteps.map((steps, idx) => { - if (tourState.currentTourStep !== idx + 1) return null; + const stepCount = idx + 1; + if (tourState.currentTourStep !== stepCount) return null; + const panelProps = { + 'data-test-subj': `timeline-tour-step-${idx + 1}`, + }; return ( { content={steps.content} anchor={`#${steps.anchor}`} subtitle={tourConfig.tourSubtitle} - footerAction={getFooterAction(steps.step)} + footerAction={getFooterAction(stepCount)} /> ); })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/step_config.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/step_config.tsx index fe668d4e5d9e..8b1e416324b1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/step_config.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/step_config.tsx @@ -8,6 +8,7 @@ import { EuiText, EuiCode } from '@elastic/eui'; import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; +import { TimelineTabs } from '../../../../../common/types'; import * as i18n from './translations'; export const TIMELINE_TOUR_CONFIG_ANCHORS = { @@ -15,17 +16,17 @@ export const TIMELINE_TOUR_CONFIG_ANCHORS = { DATA_VIEW: 'timeline-data-view', DATA_PROVIDER: 'toggle-data-provider', SAVE_TIMELINE: 'save-timeline-action', + ADD_TO_FAVORITES: 'add-to-favorites', }; export const timelineTourSteps = [ { - step: 1, title: i18n.TIMELINE_TOUR_TIMELINE_ACTIONS_STEP_TITLE, content: ( {i18n.TIMELINE_TOUR_NEW}, openButton: {i18n.TIMELINE_TOUR_OPEN}, @@ -36,13 +37,25 @@ export const timelineTourSteps = [ anchor: TIMELINE_TOUR_CONFIG_ANCHORS.ACTION_MENU, }, { - step: 2, + title: i18n.TIMELINE_TOUR_ADD_TO_FAVORITES_STEP_TITLE, + content: ( + + + + ), + anchor: TIMELINE_TOUR_CONFIG_ANCHORS.ADD_TO_FAVORITES, + }, + { + timelineTab: TimelineTabs.query, title: i18n.TIMELINE_TOUR_CHANGE_DATA_VIEW_TITLE, content: ( {i18n.TIMELINE_TOUR_DATA_VIEW}, }} @@ -52,19 +65,18 @@ export const timelineTourSteps = [ anchor: TIMELINE_TOUR_CONFIG_ANCHORS.DATA_VIEW, }, { - step: 3, + timelineTab: TimelineTabs.query, title: i18n.TIMELINE_TOUR_DATA_PROVIDER_VISIBILITY_TITLE, content: {i18n.TIMELINE_TOUR_DATA_PROVIDER_VISIBILITY_DESCRIPTION}, anchor: TIMELINE_TOUR_CONFIG_ANCHORS.DATA_PROVIDER, }, { - step: 4, title: i18n.TIMELINE_TOUR_SAVE_TIMELINE_STEP_TITLE, content: ( {i18n.TIMELINE_TOUR_SAVE}, editButton: {i18n.TIMELINE_TOUR_EDIT}, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/translations.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/translations.ts index 3b51ad38a676..3bb3b0e7eac0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/translations.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/translations.ts @@ -31,7 +31,7 @@ export const TIMELINE_TOUR_DATA_PROVIDER_VISIBILITY_TITLE = i18n.translate( 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', + defaultMessage: 'Click to expand or collapse the query builder.', } ); @@ -49,6 +49,13 @@ export const TIMELINE_TOUR_CHANGE_DATA_VIEW_TITLE = i18n.translate( } ); +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', }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timeline_templates/creation.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timeline_templates/creation.cy.ts index 9054684c53e8..b4e208191d68 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timeline_templates/creation.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timeline_templates/creation.cy.ts @@ -39,6 +39,7 @@ import { clickingOnCreateTemplateFromTimelineBtn, closeTimeline, createNewTimelineTemplate, + createTimelineTemplateOptionsPopoverBottomBar, expandEventAction, markAsFavorite, openTimelineTemplateFromSettings, @@ -113,4 +114,10 @@ describe('Timeline Templates', { tags: ['@ess', '@serverless'] }, () => { cy.get(TIMELINE_FLYOUT_WRAPPER).should('have.css', 'visibility', 'visible'); cy.get(TIMELINE_QUERY).should('have.text', getTimeline().query); }); + + it('should create timeline template from bottombar', () => { + visit(TIMELINES_URL); + createTimelineTemplateOptionsPopoverBottomBar(); + cy.get(TIMELINE_TITLE).should('have.text', 'Untitled template'); + }); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/creation.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/creation.cy.ts index 9857e24e5b33..b3c1361c517a 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/creation.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/creation.cy.ts @@ -137,10 +137,7 @@ describe('Timelines', { tags: ['@ess', '@serverless'] }, (): void => { addNameToTimelineAndSave('Test'); // Saved - cy.get(TIMELINE_STATUS).should('be.visible'); - cy.get(TIMELINE_STATUS) - .invoke('text') - .should('match', /^Saved/); + cy.get(TIMELINE_STATUS).should('not.exist'); // Offsetting the extra save that is happening in the background // for the saved search object. @@ -153,7 +150,7 @@ describe('Timelines', { tags: ['@ess', '@serverless'] }, (): void => { cy.get(TIMELINE_STATUS).should('be.visible'); cy.get(TIMELINE_STATUS) .invoke('text') - .should('match', /^Has unsaved changes/); + .should('match', /^Unsaved changes/); }); it('should save timelines as new', () => { diff --git a/x-pack/test/security_solution_cypress/cypress/screens/timeline.ts b/x-pack/test/security_solution_cypress/cypress/screens/timeline.ts index fd5f2fb57107..9480fa92e4ee 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/timeline.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/timeline.ts @@ -226,7 +226,7 @@ export const TIMELINE_PANEL = `[data-test-subj="timeline-flyout-header-panel"]`; export const TIMELINE_QUERY = '[data-test-subj="timelineQueryInput"]'; -export const TIMELINE_SETTINGS_ICON = '[data-test-subj="settings-plus-in-circle"]'; +export const TIMELINE_SETTINGS_ICON = '[data-test-subj="timeline-create-open-control"]'; export const TIMELINE_SEARCH_OR_FILTER = '[data-test-subj="timeline-select-search-or-filter"]'; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts b/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts index 43d27d7e409a..07cfee64e0f8 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/timeline.ts @@ -327,6 +327,30 @@ export const openCreateTimelineOptionsPopover = () => { cy.get(NEW_TIMELINE_ACTION).filter(':visible').should('be.visible').click(); }; +export const createTimelineOptionsPopoverBottomBar = () => { + recurse( + () => { + cy.get(TIMELINE_SETTINGS_ICON).filter(':visible').should('be.visible').click(); + return cy.get(CREATE_NEW_TIMELINE).eq(0); + }, + (sub) => sub.is(':visible') + ); + + cy.get(CREATE_NEW_TIMELINE).eq(0).should('be.visible').click(); +}; + +export const createTimelineTemplateOptionsPopoverBottomBar = () => { + recurse( + () => { + cy.get(TIMELINE_SETTINGS_ICON).filter(':visible').should('be.visible').click(); + return cy.get(CREATE_NEW_TIMELINE_TEMPLATE).eq(0); + }, + (sub) => sub.is(':visible') + ); + + cy.get(CREATE_NEW_TIMELINE_TEMPLATE).eq(0).should('be.visible').click(); +}; + export const closeCreateTimelineOptionsPopover = () => { cy.get(TIMELINE_SETTINGS_ICON).filter(':visible').should('be.visible').type('{esc}'); };