mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
# Backport This will backport the following commits from `main` to `8.15`: - [[Security Solution] Fix - Notes Flyout Product Feedback (#188129)](https://github.com/elastic/kibana/pull/188129) <!--- Backport version: 8.9.8 --> ### 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-07-12T17:20:19Z","message":"[Security Solution] Fix - Notes Flyout Product Feedback (#188129)\n\n# Summary\r\n\r\nFixes below bugs based on feedback from @paulewing.\r\n\r\n\r\n## Event Details Toggle in Notes\r\n\r\n@paulewing requested to remove the event toggle \r\n\r\n|Before|After|\r\n|---|---|\r\n||\r\n|\r\n\r\n\r\n## Notes Flyout remains open when switching tabs\r\n|Before|After|\r\n|---|---|\r\n|<video\r\nsrc=\"0e010c22
-4539-4428-9b1b-3b323a9f491c\"\r\n/>|\r\n\r\n\r\n## Notes Flyout should be resizable\r\n\r\nAs shown in above video, notes flyout is now resizable.","sha":"309b907e59df245236c24f7a3b121488da9dc3e4","branchLabelMapping":{"^v8.16.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Feature:Timeline","Team:Threat Hunting:Investigations","backport:prev-minor","v8.16.0"],"number":188129,"url":"https://github.com/elastic/kibana/pull/188129","mergeCommit":{"message":"[Security Solution] Fix - Notes Flyout Product Feedback (#188129)\n\n# Summary\r\n\r\nFixes below bugs based on feedback from @paulewing.\r\n\r\n\r\n## Event Details Toggle in Notes\r\n\r\n@paulewing requested to remove the event toggle \r\n\r\n|Before|After|\r\n|---|---|\r\n||\r\n|\r\n\r\n\r\n## Notes Flyout remains open when switching tabs\r\n|Before|After|\r\n|---|---|\r\n|<video\r\nsrc=\"0e010c22
-4539-4428-9b1b-3b323a9f491c\"\r\n/>|\r\n\r\n\r\n## Notes Flyout should be resizable\r\n\r\nAs shown in above video, notes flyout is now resizable.","sha":"309b907e59df245236c24f7a3b121488da9dc3e4"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v8.16.0","labelRegex":"^v8.16.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/188129","number":188129,"mergeCommit":{"message":"[Security Solution] Fix - Notes Flyout Product Feedback (#188129)\n\n# Summary\r\n\r\nFixes below bugs based on feedback from @paulewing.\r\n\r\n\r\n## Event Details Toggle in Notes\r\n\r\n@paulewing requested to remove the event toggle \r\n\r\n|Before|After|\r\n|---|---|\r\n||\r\n|\r\n\r\n\r\n## Notes Flyout remains open when switching tabs\r\n|Before|After|\r\n|---|---|\r\n|<video\r\nsrc=\"0e010c22
-4539-4428-9b1b-3b323a9f491c\"\r\n/>|\r\n\r\n\r\n## Notes Flyout should be resizable\r\n\r\nAs shown in above video, notes flyout is now resizable.","sha":"309b907e59df245236c24f7a3b121488da9dc3e4"}}]}] BACKPORT-->
This commit is contained in:
parent
02533fa7e7
commit
bb0aeff31a
10 changed files with 356 additions and 355 deletions
|
@ -51,6 +51,7 @@ export interface NoteCardsProps {
|
|||
eventId?: string;
|
||||
timelineId: string;
|
||||
onCancel?: () => void;
|
||||
showToggleEventDetailsAction?: boolean;
|
||||
}
|
||||
|
||||
/** A view for entering and reviewing notes */
|
||||
|
@ -65,6 +66,7 @@ export const NoteCards = React.memo<NoteCardsProps>(
|
|||
eventId,
|
||||
timelineId,
|
||||
onCancel,
|
||||
showToggleEventDetailsAction = true,
|
||||
}) => {
|
||||
const [newNote, setNewNote] = useState('');
|
||||
|
||||
|
@ -109,7 +111,11 @@ export const NoteCards = React.memo<NoteCardsProps>(
|
|||
<EuiScreenReaderOnly data-test-subj="screenReaderOnly">
|
||||
<p>{i18n.YOU_ARE_VIEWING_NOTES(ariaRowindex)}</p>
|
||||
</EuiScreenReaderOnly>
|
||||
<NotePreviews timelineId={timelineId} notes={notes} />
|
||||
<NotePreviews
|
||||
timelineId={timelineId}
|
||||
notes={notes}
|
||||
showToggleEventDetailsAction={showToggleEventDetailsAction}
|
||||
/>
|
||||
</NotesContainer>
|
||||
</NotePreviewsContainer>
|
||||
) : null}
|
||||
|
|
|
@ -239,6 +239,63 @@ describe('NotePreviews', () => {
|
|||
expect(wrapper.find('[data-test-subj="delete-note"] button').prop('disabled')).toBeFalsy();
|
||||
});
|
||||
|
||||
test('should render toggle event details action by default', () => {
|
||||
const timeline = mockTimelineResults[0];
|
||||
(useDeepEqualSelector as jest.Mock).mockReturnValue(timeline);
|
||||
|
||||
const wrapper = mountWithI18nProvider(
|
||||
<TestProviders>
|
||||
<NotePreviews
|
||||
notes={[
|
||||
{
|
||||
noteId: 'noteId1',
|
||||
note: 'enabled delete',
|
||||
savedObjectId: 'test-id',
|
||||
updated: note2updated,
|
||||
updatedBy: 'alice',
|
||||
},
|
||||
]}
|
||||
showTimelineDescription
|
||||
timelineId="test-timeline-id"
|
||||
/>
|
||||
</TestProviders>,
|
||||
{
|
||||
wrappingComponent: createReactQueryWrapper(),
|
||||
}
|
||||
);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="notes-toggle-event-details"]').exists()).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should not render toggle event details action when showToggleEventDetailsAction is false ', () => {
|
||||
const timeline = mockTimelineResults[0];
|
||||
(useDeepEqualSelector as jest.Mock).mockReturnValue(timeline);
|
||||
|
||||
const wrapper = mountWithI18nProvider(
|
||||
<TestProviders>
|
||||
<NotePreviews
|
||||
notes={[
|
||||
{
|
||||
noteId: 'noteId1',
|
||||
note: 'enabled delete',
|
||||
savedObjectId: 'test-id',
|
||||
updated: note2updated,
|
||||
updatedBy: 'alice',
|
||||
},
|
||||
]}
|
||||
showTimelineDescription
|
||||
timelineId="test-timeline-id"
|
||||
showToggleEventDetailsAction={false}
|
||||
/>
|
||||
</TestProviders>,
|
||||
{
|
||||
wrappingComponent: createReactQueryWrapper(),
|
||||
}
|
||||
);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="notes-toggle-event-details"]').exists()).toBeFalsy();
|
||||
});
|
||||
|
||||
describe('Delete Notes', () => {
|
||||
it('should delete note correctly', async () => {
|
||||
const timeline = {
|
||||
|
|
|
@ -101,6 +101,7 @@ const ToggleEventDetailsButtonComponent: React.FC<ToggleEventDetailsButtonProps>
|
|||
|
||||
return (
|
||||
<EuiButtonIcon
|
||||
data-test-subj="notes-toggle-event-details"
|
||||
title={i18n.TOGGLE_EXPAND_EVENT_DETAILS}
|
||||
aria-label={i18n.TOGGLE_EXPAND_EVENT_DETAILS}
|
||||
color="text"
|
||||
|
@ -204,10 +205,32 @@ const NoteActions = React.memo<{
|
|||
savedObjectId?: string | null;
|
||||
confirmingNoteId?: string | null;
|
||||
eventIdToNoteIds?: Record<string, string[]>;
|
||||
}>(({ eventId, timelineId, noteId, confirmingNoteId, eventIdToNoteIds, savedObjectId }) => {
|
||||
return eventId && timelineId ? (
|
||||
<>
|
||||
<ToggleEventDetailsButton eventId={eventId} timelineId={timelineId} />
|
||||
showToggleEventDetailsAction?: boolean;
|
||||
}>(
|
||||
({
|
||||
eventId,
|
||||
timelineId,
|
||||
noteId,
|
||||
confirmingNoteId,
|
||||
eventIdToNoteIds,
|
||||
savedObjectId,
|
||||
showToggleEventDetailsAction = true,
|
||||
}) => {
|
||||
return eventId && timelineId ? (
|
||||
<>
|
||||
{showToggleEventDetailsAction ? (
|
||||
<ToggleEventDetailsButton eventId={eventId} timelineId={timelineId} />
|
||||
) : null}
|
||||
<DeleteNoteButton
|
||||
noteId={noteId}
|
||||
eventId={eventId}
|
||||
confirmingNoteId={confirmingNoteId}
|
||||
savedObjectId={savedObjectId}
|
||||
timelineId={timelineId}
|
||||
eventIdToNoteIds={eventIdToNoteIds}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<DeleteNoteButton
|
||||
noteId={noteId}
|
||||
eventId={eventId}
|
||||
|
@ -216,18 +239,9 @@ const NoteActions = React.memo<{
|
|||
timelineId={timelineId}
|
||||
eventIdToNoteIds={eventIdToNoteIds}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<DeleteNoteButton
|
||||
noteId={noteId}
|
||||
eventId={eventId}
|
||||
confirmingNoteId={confirmingNoteId}
|
||||
savedObjectId={savedObjectId}
|
||||
timelineId={timelineId}
|
||||
eventIdToNoteIds={eventIdToNoteIds}
|
||||
/>
|
||||
);
|
||||
});
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
NoteActions.displayName = 'NoteActions';
|
||||
/**
|
||||
|
@ -238,10 +252,11 @@ interface NotePreviewsProps {
|
|||
notes?: TimelineResultNote[] | null;
|
||||
timelineId?: string;
|
||||
showTimelineDescription?: boolean;
|
||||
showToggleEventDetailsAction?: boolean;
|
||||
}
|
||||
|
||||
export const NotePreviews = React.memo<NotePreviewsProps>(
|
||||
({ notes, timelineId, showTimelineDescription }) => {
|
||||
({ notes, timelineId, showTimelineDescription, showToggleEventDetailsAction = true }) => {
|
||||
const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []);
|
||||
const getTimelineNotes = useMemo(() => getTimelineNoteSelector(), []);
|
||||
const timeline = useDeepEqualSelector((state) =>
|
||||
|
@ -315,6 +330,7 @@ export const NotePreviews = React.memo<NotePreviewsProps>(
|
|||
savedObjectId={note.savedObjectId}
|
||||
confirmingNoteId={timeline?.confirmingNoteId}
|
||||
eventIdToNoteIds={eventIdToNoteIds}
|
||||
showToggleEventDetailsAction={showToggleEventDetailsAction}
|
||||
/>
|
||||
),
|
||||
timelineAvatar: (
|
||||
|
@ -326,7 +342,13 @@ export const NotePreviews = React.memo<NotePreviewsProps>(
|
|||
),
|
||||
};
|
||||
}),
|
||||
[eventIdToNoteIds, notes, timelineId, timeline?.confirmingNoteId]
|
||||
[
|
||||
eventIdToNoteIds,
|
||||
notes,
|
||||
timelineId,
|
||||
timeline?.confirmingNoteId,
|
||||
showToggleEventDetailsAction,
|
||||
]
|
||||
);
|
||||
|
||||
const commentList = useMemo(
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
|
||||
import React from 'react';
|
||||
import {
|
||||
EuiFlyout,
|
||||
EuiFlyoutBody,
|
||||
EuiFlyoutHeader,
|
||||
EuiFlyoutResizable,
|
||||
EuiOutsideClickDetector,
|
||||
EuiTitle,
|
||||
useGeneratedHtmlId,
|
||||
} from '@elastic/eui';
|
||||
|
@ -32,7 +33,7 @@ export type NotesFlyoutProps = {
|
|||
* z-index override is needed because otherwise NotesFlyout appears below
|
||||
* Timeline Modal as they both have same z-index of 1000
|
||||
*/
|
||||
const NotesFlyoutContainer = styled(EuiFlyout)`
|
||||
const NotesFlyoutContainer = styled(EuiFlyoutResizable)`
|
||||
/*
|
||||
* We want the width of flyout to be less than 50% of screen because
|
||||
* otherwise it interferes with the delete notes modal
|
||||
|
@ -55,33 +56,37 @@ export const NotesFlyout = React.memo(function NotesFlyout(props: NotesFlyoutPro
|
|||
}
|
||||
|
||||
return (
|
||||
<NotesFlyoutContainer
|
||||
ownFocus={false}
|
||||
className="timeline-notes-flyout"
|
||||
data-test-subj="timeline-notes-flyout"
|
||||
onClose={onClose}
|
||||
aria-labelledby={notesFlyoutTitleId}
|
||||
maxWidth={750}
|
||||
>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<EuiTitle size="m">
|
||||
<h2>{i18n.NOTES}</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<NoteCards
|
||||
ariaRowindex={0}
|
||||
associateNote={associateNote}
|
||||
className="notes-in-flyout"
|
||||
data-test-subj="note-cards"
|
||||
notes={notes}
|
||||
showAddNote={true}
|
||||
toggleShowAddNote={toggleShowAddNote}
|
||||
eventId={eventId}
|
||||
timelineId={timelineId}
|
||||
onCancel={onCancel}
|
||||
/>
|
||||
</EuiFlyoutBody>
|
||||
</NotesFlyoutContainer>
|
||||
<EuiOutsideClickDetector onOutsideClick={onClose}>
|
||||
<NotesFlyoutContainer
|
||||
ownFocus={false}
|
||||
className="timeline-notes-flyout"
|
||||
data-test-subj="timeline-notes-flyout"
|
||||
onClose={onClose}
|
||||
aria-labelledby={notesFlyoutTitleId}
|
||||
minWidth={500}
|
||||
maxWidth={1400}
|
||||
>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<EuiTitle size="m">
|
||||
<h2>{i18n.NOTES}</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<NoteCards
|
||||
ariaRowindex={0}
|
||||
associateNote={associateNote}
|
||||
className="notes-in-flyout"
|
||||
data-test-subj="note-cards"
|
||||
notes={notes}
|
||||
showAddNote={true}
|
||||
toggleShowAddNote={toggleShowAddNote}
|
||||
eventId={eventId}
|
||||
timelineId={timelineId}
|
||||
onCancel={onCancel}
|
||||
showToggleEventDetailsAction={false}
|
||||
/>
|
||||
</EuiFlyoutBody>
|
||||
</NotesFlyoutContainer>
|
||||
</EuiOutsideClickDetector>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { TimelineId } from '../../../../../common/types';
|
||||
import { TimelineId, TimelineTabs } from '../../../../../common/types';
|
||||
import { renderHook, act } from '@testing-library/react-hooks/dom';
|
||||
import { createMockStore, mockGlobalState, TestProviders } from '../../../../common/mock';
|
||||
import type { UseNotesInFlyoutArgs } from './use_notes_in_flyout';
|
||||
import { useNotesInFlyout } from './use_notes_in_flyout';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
@ -85,11 +86,13 @@ const refetchMock = jest.fn();
|
|||
|
||||
const renderTestHook = () => {
|
||||
return renderHook(
|
||||
() =>
|
||||
(props?: Partial<UseNotesInFlyoutArgs>) =>
|
||||
useNotesInFlyout({
|
||||
eventIdToNoteIds: mockEventIdToNoteIds,
|
||||
timelineId: TimelineId.test,
|
||||
refetch: refetchMock,
|
||||
activeTab: TimelineTabs.query,
|
||||
...props,
|
||||
}),
|
||||
{
|
||||
wrapper: ({ children }) => (
|
||||
|
@ -198,4 +201,33 @@ describe('useNotesInFlyout', () => {
|
|||
|
||||
expect(result.current.isNotesFlyoutVisible).toBe(false);
|
||||
});
|
||||
|
||||
it('should close the flyout when activeTab is changed', () => {
|
||||
const { result, rerender, waitForNextUpdate } = renderTestHook();
|
||||
|
||||
act(() => {
|
||||
result.current.setNotesEventId('event-1');
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.showNotesFlyout();
|
||||
});
|
||||
|
||||
expect(result.current.isNotesFlyoutVisible).toBe(true);
|
||||
|
||||
act(() => {
|
||||
// no change in active Tab
|
||||
rerender({ activeTab: TimelineTabs.query });
|
||||
});
|
||||
|
||||
expect(result.current.isNotesFlyoutVisible).toBe(true);
|
||||
|
||||
act(() => {
|
||||
rerender({ activeTab: TimelineTabs.eql });
|
||||
});
|
||||
|
||||
waitForNextUpdate();
|
||||
|
||||
expect(result.current.isNotesFlyoutVisible).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,16 +5,18 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import type { TimelineTabs } from '../../../../../common/types';
|
||||
import { useDeepEqualSelector } from '../../../../common/hooks/use_selector';
|
||||
import { appSelectors } from '../../../../common/store';
|
||||
import { timelineActions } from '../../../store';
|
||||
|
||||
interface UseNotesInFlyoutArgs {
|
||||
export interface UseNotesInFlyoutArgs {
|
||||
eventIdToNoteIds: Record<string, string[]>;
|
||||
refetch?: () => void;
|
||||
timelineId: string;
|
||||
activeTab: TimelineTabs;
|
||||
}
|
||||
|
||||
const EMPTY_STRING_ARRAY: string[] = [];
|
||||
|
@ -36,12 +38,19 @@ export const useNotesInFlyout = (args: UseNotesInFlyoutArgs) => {
|
|||
setIsNotesFlyoutVisible(true);
|
||||
}, []);
|
||||
|
||||
const { eventIdToNoteIds, refetch, timelineId } = args;
|
||||
const { eventIdToNoteIds, refetch, timelineId, activeTab } = args;
|
||||
|
||||
const getNotesByIds = useMemo(() => appSelectors.notesByIdsSelector(), []);
|
||||
|
||||
const notesById = useDeepEqualSelector(getNotesByIds);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeTab) {
|
||||
// if activeTab changes, close the notes flyout
|
||||
closeNotesFlyout();
|
||||
}
|
||||
}, [activeTab, closeNotesFlyout]);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const noteIds: string[] = useMemo(
|
||||
|
|
|
@ -163,6 +163,7 @@ export const EqlTabContentComponent: React.FC<Props> = ({
|
|||
eventIdToNoteIds,
|
||||
refetch,
|
||||
timelineId,
|
||||
activeTab: TimelineTabs.eql,
|
||||
});
|
||||
|
||||
const onToggleShowNotes = useCallback(
|
||||
|
|
|
@ -199,6 +199,7 @@ export const PinnedTabContentComponent: React.FC<Props> = ({
|
|||
eventIdToNoteIds,
|
||||
refetch,
|
||||
timelineId,
|
||||
activeTab: TimelineTabs.pinned,
|
||||
});
|
||||
|
||||
const onToggleShowNotes = useCallback(
|
||||
|
|
|
@ -229,6 +229,7 @@ export const QueryTabContentComponent: React.FC<Props> = ({
|
|||
eventIdToNoteIds,
|
||||
refetch,
|
||||
timelineId,
|
||||
activeTab,
|
||||
});
|
||||
|
||||
const onToggleShowNotes = useCallback(
|
||||
|
|
|
@ -27,7 +27,6 @@ import { createStartServicesMock } from '../../../../../common/lib/kibana/kibana
|
|||
import type { StartServices } from '../../../../../types';
|
||||
import { useKibana } from '../../../../../common/lib/kibana';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { timelineActions } from '../../../../store';
|
||||
import type { ExperimentalFeatures } from '../../../../../../common';
|
||||
import { allowedExperimentalValues } from '../../../../../../common';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
|
||||
|
@ -35,7 +34,7 @@ import { defaultUdtHeaders } from '../../unified_components/default_headers';
|
|||
import { defaultColumnHeaderType } from '../../body/column_headers/default_headers';
|
||||
import { useUserPrivileges } from '../../../../../common/components/user_privileges';
|
||||
import { getEndpointPrivilegesInitialStateMock } from '../../../../../common/components/user_privileges/endpoint/mocks';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import * as timelineActions from '../../../../store/actions';
|
||||
|
||||
jest.mock('../../../../../common/components/user_privileges');
|
||||
|
||||
|
@ -172,6 +171,10 @@ describe('query tab with unified timeline', () => {
|
|||
});
|
||||
|
||||
beforeEach(() => {
|
||||
Object.defineProperty(window, '__@hello-pangea/dnd-disable-dev-warnings', {
|
||||
value: true,
|
||||
writable: false,
|
||||
});
|
||||
useTimelineEventsMock = jest.fn(() => [
|
||||
false,
|
||||
{
|
||||
|
@ -776,332 +779,196 @@ describe('query tab with unified timeline', () => {
|
|||
|
||||
describe('Leading actions - notes', () => {
|
||||
describe('securitySolutionNotesEnabled = true', () => {
|
||||
describe('expandableFlyoutDisabled = false', () => {
|
||||
beforeEach(() => {
|
||||
(useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(
|
||||
jest.fn((feature: keyof ExperimentalFeatures) => {
|
||||
if (feature === 'unifiedComponentsInTimelineEnabled') {
|
||||
return true;
|
||||
}
|
||||
if (feature === 'securitySolutionNotesEnabled') {
|
||||
return true;
|
||||
}
|
||||
return allowedExperimentalValues[feature];
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it(
|
||||
'should have the notification dot & correct tooltip',
|
||||
async () => {
|
||||
renderTestComponents();
|
||||
|
||||
expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
|
||||
|
||||
expect(screen.getAllByTestId('timeline-notes-button-small')).toHaveLength(1);
|
||||
expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
|
||||
|
||||
expect(screen.getByTestId('timeline-notes-notification-dot')).toBeVisible();
|
||||
|
||||
fireEvent.mouseOver(screen.getByTestId('timeline-notes-button-small'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('timeline-notes-tool-tip')).toBeVisible();
|
||||
expect(screen.getByTestId('timeline-notes-tool-tip')).toHaveTextContent(
|
||||
'1 Note available. Click to view it & add more.'
|
||||
);
|
||||
});
|
||||
},
|
||||
SPECIAL_TEST_TIMEOUT
|
||||
);
|
||||
it(
|
||||
'should be able to add notes through expandable flyout',
|
||||
async () => {
|
||||
renderTestComponents();
|
||||
expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockOpenFlyout).toHaveBeenCalled();
|
||||
});
|
||||
},
|
||||
SPECIAL_TEST_TIMEOUT
|
||||
beforeEach(() => {
|
||||
(useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(
|
||||
jest.fn((feature: keyof ExperimentalFeatures) => {
|
||||
if (feature === 'unifiedComponentsInTimelineEnabled') {
|
||||
return true;
|
||||
}
|
||||
if (feature === 'securitySolutionNotesEnabled') {
|
||||
return true;
|
||||
}
|
||||
return allowedExperimentalValues[feature];
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
describe('expandableFlyoutDisabled = true', () => {
|
||||
beforeEach(() => {
|
||||
(useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(
|
||||
jest.fn((feature: keyof ExperimentalFeatures) => {
|
||||
if (feature === 'unifiedComponentsInTimelineEnabled') {
|
||||
return true;
|
||||
}
|
||||
if (feature === 'expandableFlyoutDisabled') {
|
||||
return true;
|
||||
}
|
||||
if (feature === 'securitySolutionNotesEnabled') {
|
||||
return true;
|
||||
}
|
||||
return allowedExperimentalValues[feature];
|
||||
})
|
||||
);
|
||||
});
|
||||
it(
|
||||
'should have the notification dot & correct tooltip',
|
||||
async () => {
|
||||
renderTestComponents();
|
||||
|
||||
it(
|
||||
'should have the notification dot & correct tooltip',
|
||||
async () => {
|
||||
renderTestComponents();
|
||||
expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
|
||||
|
||||
expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
|
||||
expect(screen.getAllByTestId('timeline-notes-button-small')).toHaveLength(1);
|
||||
expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
|
||||
|
||||
expect(screen.getAllByTestId('timeline-notes-button-small')).toHaveLength(1);
|
||||
expect(screen.getByTestId('timeline-notes-notification-dot')).toBeVisible();
|
||||
|
||||
fireEvent.mouseOver(screen.getByTestId('timeline-notes-button-small'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('timeline-notes-tool-tip')).toBeVisible();
|
||||
expect(screen.getByTestId('timeline-notes-tool-tip')).toHaveTextContent(
|
||||
'1 Note available. Click to view it & add more.'
|
||||
);
|
||||
});
|
||||
},
|
||||
SPECIAL_TEST_TIMEOUT
|
||||
);
|
||||
it(
|
||||
'should be able to add notes through expandable flyout',
|
||||
async () => {
|
||||
renderTestComponents();
|
||||
expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
|
||||
});
|
||||
|
||||
expect(screen.getByTestId('timeline-notes-notification-dot')).toBeVisible();
|
||||
fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
|
||||
|
||||
fireEvent.mouseOver(screen.getByTestId('timeline-notes-button-small'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('timeline-notes-tool-tip')).toBeVisible();
|
||||
expect(screen.getByTestId('timeline-notes-tool-tip')).toHaveTextContent(
|
||||
'1 Note available. Click to view it & add more.'
|
||||
);
|
||||
});
|
||||
},
|
||||
SPECIAL_TEST_TIMEOUT
|
||||
);
|
||||
it(
|
||||
'should be able to add notes using EuiFlyout',
|
||||
async () => {
|
||||
renderTestComponents();
|
||||
expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('add-note-container')).toBeVisible();
|
||||
});
|
||||
},
|
||||
SPECIAL_TEST_TIMEOUT
|
||||
);
|
||||
|
||||
it(
|
||||
'should be cancel adding notes',
|
||||
async () => {
|
||||
renderTestComponents();
|
||||
expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('add-note-container')).toBeVisible();
|
||||
});
|
||||
|
||||
userEvent.type(screen.getByTestId('euiMarkdownEditorTextArea'), 'Test Note 1');
|
||||
|
||||
expect(screen.getByTestId('cancel')).not.toBeDisabled();
|
||||
|
||||
fireEvent.click(screen.getByTestId('cancel'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId('add-note-container')).not.toBeInTheDocument();
|
||||
});
|
||||
},
|
||||
SPECIAL_TEST_TIMEOUT
|
||||
);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(mockOpenFlyout).toHaveBeenCalled();
|
||||
});
|
||||
},
|
||||
SPECIAL_TEST_TIMEOUT
|
||||
);
|
||||
});
|
||||
|
||||
describe('securitySolutionNotesEnabled = false', () => {
|
||||
describe('expandableFlyoutDisabled = false', () => {
|
||||
beforeEach(() => {
|
||||
(useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(
|
||||
jest.fn((feature: keyof ExperimentalFeatures) => {
|
||||
if (feature === 'unifiedComponentsInTimelineEnabled') {
|
||||
return true;
|
||||
}
|
||||
if (feature === 'securitySolutionNotesEnabled') {
|
||||
return false;
|
||||
}
|
||||
return allowedExperimentalValues[feature];
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it(
|
||||
'should have the notification dot & correct tooltip',
|
||||
async () => {
|
||||
renderTestComponents();
|
||||
|
||||
expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
|
||||
|
||||
expect(screen.getAllByTestId('timeline-notes-button-small')).toHaveLength(1);
|
||||
expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
|
||||
|
||||
expect(screen.getByTestId('timeline-notes-notification-dot')).toBeVisible();
|
||||
|
||||
fireEvent.mouseOver(screen.getByTestId('timeline-notes-button-small'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('timeline-notes-tool-tip')).toBeVisible();
|
||||
expect(screen.getByTestId('timeline-notes-tool-tip')).toHaveTextContent(
|
||||
'1 Note available. Click to view it & add more.'
|
||||
);
|
||||
});
|
||||
},
|
||||
SPECIAL_TEST_TIMEOUT
|
||||
);
|
||||
it(
|
||||
'should be able to add notes using EuiFlyout',
|
||||
async () => {
|
||||
renderTestComponents();
|
||||
expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('add-note-container')).toBeVisible();
|
||||
});
|
||||
},
|
||||
SPECIAL_TEST_TIMEOUT
|
||||
);
|
||||
|
||||
it(
|
||||
'should be cancel adding notes',
|
||||
async () => {
|
||||
renderTestComponents();
|
||||
expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('add-note-container')).toBeVisible();
|
||||
});
|
||||
|
||||
userEvent.type(screen.getByTestId('euiMarkdownEditorTextArea'), 'Test Note 1');
|
||||
|
||||
expect(screen.getByTestId('cancel')).not.toBeDisabled();
|
||||
|
||||
fireEvent.click(screen.getByTestId('cancel'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId('add-note-container')).not.toBeInTheDocument();
|
||||
});
|
||||
},
|
||||
SPECIAL_TEST_TIMEOUT
|
||||
beforeEach(() => {
|
||||
(useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(
|
||||
jest.fn((feature: keyof ExperimentalFeatures) => {
|
||||
if (feature === 'unifiedComponentsInTimelineEnabled') {
|
||||
return true;
|
||||
}
|
||||
if (feature === 'securitySolutionNotesEnabled') {
|
||||
return false;
|
||||
}
|
||||
return allowedExperimentalValues[feature];
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
describe('expandableFlyoutDisabled = true', () => {
|
||||
beforeEach(() => {
|
||||
(useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation(
|
||||
jest.fn((feature: keyof ExperimentalFeatures) => {
|
||||
if (feature === 'unifiedComponentsInTimelineEnabled') {
|
||||
return true;
|
||||
}
|
||||
if (feature === 'expandableFlyoutDisabled') {
|
||||
return true;
|
||||
}
|
||||
if (feature === 'securitySolutionNotesEnabled') {
|
||||
return true;
|
||||
}
|
||||
return allowedExperimentalValues[feature];
|
||||
})
|
||||
);
|
||||
});
|
||||
it(
|
||||
'should have the notification dot & correct tooltip',
|
||||
async () => {
|
||||
renderTestComponents();
|
||||
|
||||
it(
|
||||
'should have the notification dot & correct tooltip',
|
||||
async () => {
|
||||
renderTestComponents();
|
||||
expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
|
||||
|
||||
expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
|
||||
expect(screen.getAllByTestId('timeline-notes-button-small')).toHaveLength(1);
|
||||
expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
|
||||
|
||||
expect(screen.getAllByTestId('timeline-notes-button-small')).toHaveLength(1);
|
||||
expect(screen.getByTestId('timeline-notes-notification-dot')).toBeVisible();
|
||||
|
||||
fireEvent.mouseOver(screen.getByTestId('timeline-notes-button-small'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('timeline-notes-tool-tip')).toBeVisible();
|
||||
expect(screen.getByTestId('timeline-notes-tool-tip')).toHaveTextContent(
|
||||
'1 Note available. Click to view it & add more.'
|
||||
);
|
||||
});
|
||||
},
|
||||
SPECIAL_TEST_TIMEOUT
|
||||
);
|
||||
it(
|
||||
'should be able to add notes using EuiFlyout',
|
||||
async () => {
|
||||
renderTestComponents();
|
||||
expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
|
||||
});
|
||||
|
||||
expect(screen.getByTestId('timeline-notes-notification-dot')).toBeVisible();
|
||||
fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
|
||||
|
||||
fireEvent.mouseOver(screen.getByTestId('timeline-notes-button-small'));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('add-note-container')).toBeVisible();
|
||||
});
|
||||
},
|
||||
SPECIAL_TEST_TIMEOUT
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('timeline-notes-tool-tip')).toBeVisible();
|
||||
expect(screen.getByTestId('timeline-notes-tool-tip')).toHaveTextContent(
|
||||
'1 Note available. Click to view it & add more.'
|
||||
);
|
||||
it(
|
||||
'should cancel adding notes',
|
||||
async () => {
|
||||
renderTestComponents();
|
||||
expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('add-note-container')).toBeVisible();
|
||||
});
|
||||
|
||||
expect(screen.getByTestId('cancel')).not.toBeDisabled();
|
||||
|
||||
fireEvent.click(screen.getByTestId('cancel'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId('add-note-container')).not.toBeInTheDocument();
|
||||
});
|
||||
},
|
||||
SPECIAL_TEST_TIMEOUT
|
||||
);
|
||||
|
||||
it(
|
||||
'should be able to delete notes',
|
||||
async () => {
|
||||
renderTestComponents();
|
||||
expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('delete-note')).toBeVisible();
|
||||
});
|
||||
|
||||
const noteDeleteSpy = jest.spyOn(timelineActions, 'setConfirmingNoteId');
|
||||
|
||||
fireEvent.click(screen.getByTestId('delete-note'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(noteDeleteSpy).toHaveBeenCalled();
|
||||
expect(noteDeleteSpy).toHaveBeenCalledWith({
|
||||
confirmingNoteId: '1',
|
||||
id: TimelineId.test,
|
||||
});
|
||||
},
|
||||
SPECIAL_TEST_TIMEOUT
|
||||
);
|
||||
it(
|
||||
'should be able to add notes using EuiFlyout',
|
||||
async () => {
|
||||
renderTestComponents();
|
||||
expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
|
||||
});
|
||||
},
|
||||
SPECIAL_TEST_TIMEOUT
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
|
||||
});
|
||||
it(
|
||||
'should not show toggle event details action',
|
||||
async () => {
|
||||
renderTestComponents();
|
||||
expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
|
||||
|
||||
fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('add-note-container')).toBeVisible();
|
||||
});
|
||||
},
|
||||
SPECIAL_TEST_TIMEOUT
|
||||
);
|
||||
fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
|
||||
|
||||
it(
|
||||
'should be cancel adding notes',
|
||||
async () => {
|
||||
renderTestComponents();
|
||||
expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('add-note-container')).toBeVisible();
|
||||
});
|
||||
|
||||
userEvent.type(screen.getByTestId('euiMarkdownEditorTextArea'), 'Test Note 1');
|
||||
|
||||
expect(screen.getByTestId('cancel')).not.toBeDisabled();
|
||||
|
||||
fireEvent.click(screen.getByTestId('cancel'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId('add-note-container')).not.toBeInTheDocument();
|
||||
});
|
||||
},
|
||||
SPECIAL_TEST_TIMEOUT
|
||||
);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId('notes-toggle-event-details')).not.toBeInTheDocument();
|
||||
});
|
||||
},
|
||||
SPECIAL_TEST_TIMEOUT
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue