Skip to content

Commit

Permalink
[SecuritySolution][Notes] - make sure that timeline is saved before a…
Browse files Browse the repository at this point in the history
…llowing users to save notes (#195842)
  • Loading branch information
PhilippeOberti authored Oct 14, 2024
1 parent 489c090 commit f7b808c
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ describe('AttachToActiveTimeline', () => {
[TimelineId.active]: {
...mockGlobalState.timeline.timelineById[TimelineId.test],
savedObjectId: 'savedObjectId',
status: 'active',
},
},
},
Expand Down Expand Up @@ -104,6 +105,7 @@ describe('AttachToActiveTimeline', () => {
[TimelineId.active]: {
...mockGlobalState.timeline.timelineById[TimelineId.test],
savedObjectId: 'savedObjectId',
status: 'active',
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { EuiCallOut, EuiCheckbox, EuiFlexGroup, EuiFlexItem, EuiText } from '@el
import { i18n } from '@kbn/i18n';
import { css } from '@emotion/react';
import { useSelector } from 'react-redux';
import { TimelineStatusEnum } from '../../../../../common/api/timeline';
import type { State } from '../../../../common/store';
import { TimelineId } from '../../../../../common/types';
import { SaveTimelineButton } from '../../../../timelines/components/modal/actions/save_timeline_button';
Expand Down Expand Up @@ -76,10 +77,9 @@ export const AttachToActiveTimeline = memo(
const timeline = useSelector((state: State) =>
timelineSelectors.selectTimelineById(state, TimelineId.active)
);
const timelineSavedObjectId = useMemo(() => timeline?.savedObjectId ?? '', [timeline]);
const isTimelineSaved: boolean = useMemo(
() => timelineSavedObjectId.length > 0,
[timelineSavedObjectId]
() => timeline.status === TimelineStatusEnum.active,
[timeline.status]
);

const onCheckboxChange = useCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import { Flyouts } from '../../shared/constants/flyouts';
import { TimelineId } from '../../../../../common/types';
import { ReqStatus } from '../../../../notes';
import { useBasicDataFromDetailsData } from '../../shared/hooks/use_basic_data_from_details_data';
import { TimelineStatusEnum } from '../../../../../common/api/timeline';
import type { State } from '../../../../common/store';

jest.mock('../../shared/hooks/use_which_flyout');
jest.mock('../../shared/hooks/use_basic_data_from_details_data');
Expand Down Expand Up @@ -52,7 +54,7 @@ const panelContextValue = {
dataFormattedForFieldBrowser: [],
} as unknown as DocumentDetailsContext;

const mockGlobalStateWithSavedTimeline = {
const mockGlobalStateWithSavedTimeline: State = {
...mockGlobalState,
timeline: {
...mockGlobalState.timeline,
Expand All @@ -61,6 +63,7 @@ const mockGlobalStateWithSavedTimeline = {
[TimelineId.active]: {
...mockGlobalState.timeline.timelineById[TimelineId.test],
savedObjectId: 'savedObjectId',
status: TimelineStatusEnum.active,
pinnedEventIds: {},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useDispatch, useSelector } from 'react-redux';
import { EuiFlexGroup, EuiFlexItem, EuiLoadingElastic, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useBasicDataFromDetailsData } from '../../shared/hooks/use_basic_data_from_details_data';
import type { TimelineModel } from '../../../..';
import { Flyouts } from '../../shared/constants/flyouts';
import { timelineSelectors } from '../../../../timelines/store';
import { TimelineId } from '../../../../../common/types';
Expand All @@ -21,6 +22,7 @@ import { NotesList } from '../../../../notes/components/notes_list';
import { pinEvent } from '../../../../timelines/store/actions';
import type { State } from '../../../../common/store';
import type { Note } from '../../../../../common/api/timeline';
import { TimelineStatusEnum } from '../../../../../common/api/timeline';
import {
fetchNotesByDocumentIds,
ReqStatus,
Expand Down Expand Up @@ -63,10 +65,17 @@ export const NotesDetails = memo(() => {
// if the flyout is open from a timeline and that timeline is saved, we automatically check the checkbox to associate the note to it
const isTimelineFlyout = useWhichFlyout() === Flyouts.timeline;

const timeline = useSelector((state: State) =>
const timeline: TimelineModel = useSelector((state: State) =>
timelineSelectors.selectTimelineById(state, TimelineId.active)
);
const timelineSavedObjectId = useMemo(() => timeline?.savedObjectId ?? '', [timeline]);
const timelineSavedObjectId = useMemo(
() => timeline.savedObjectId ?? '',
[timeline.savedObjectId]
);
const isTimelineSaved: boolean = useMemo(
() => timeline.status === TimelineStatusEnum.active,
[timeline.status]
);

// Automatically pin an associated event if it's attached to a timeline and it's not pinned yet
const onNoteAddInTimeline = useCallback(() => {
Expand Down Expand Up @@ -141,7 +150,7 @@ export const NotesDetails = memo(() => {
{isTimelineFlyout && (
<AttachToActiveTimeline
setAttachToTimeline={setAttachToTimeline}
isCheckboxDisabled={timelineSavedObjectId.length === 0}
isCheckboxDisabled={!isTimelineSaved}
/>
)}
</AddNote>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import React from 'react';
import { TimelineId } from '../../../../../../common/types';
import { SAVE_TIMELINE_CALLOUT_TEST_ID } from '../../../notes/test_ids';
import { useUserPrivileges } from '../../../../../common/components/user_privileges';
import { TimelineStatusEnum } from '../../../../../../common/api/timeline';
import type { State } from '../../../../../common/store';

jest.mock('../../../../../common/hooks/use_experimental_features');
jest.mock('../../../../../common/components/user_privileges');
Expand All @@ -38,7 +40,7 @@ jest.mock('react-redux', () => {
};
});

const mockGlobalStateWithSavedTimeline = {
const mockGlobalStateWithSavedTimeline: State = {
...mockGlobalState,
timeline: {
...mockGlobalState.timeline,
Expand All @@ -47,11 +49,12 @@ const mockGlobalStateWithSavedTimeline = {
[TimelineId.active]: {
...mockGlobalState.timeline.timelineById[TimelineId.test],
savedObjectId: 'savedObjectId',
status: TimelineStatusEnum.active,
},
},
},
};
const mockGlobalStateWithUnSavedTimeline = {
const mockGlobalStateWithUnSavedTimeline: State = {
...mockGlobalState,
timeline: {
...mockGlobalState.timeline,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { css } from '@emotion/react';
import { useDispatch, useSelector } from 'react-redux';
import { FormattedRelative } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import type { TimelineModel } from '../../../../..';
import { SaveTimelineCallout } from '../../../notes/save_timeline';
import { AddNote } from '../../../../../notes/components/add_note';
import { useUserPrivileges } from '../../../../../common/components/user_privileges';
Expand All @@ -40,6 +41,7 @@ import {
selectSortedNotesBySavedObjectId,
} from '../../../../../notes';
import type { Note } from '../../../../../../common/api/timeline';
import { TimelineStatusEnum } from '../../../../../../common/api/timeline';
import { NotesList } from '../../../../../notes/components/notes_list';
import { OldNotes } from '../../../notes/old_notes';
import { Participants } from '../../../notes/participants';
Expand Down Expand Up @@ -92,11 +94,16 @@ const NotesTabContentComponent: React.FC<NotesTabContentProps> = React.memo(({ t
const scrollToTop = useShallowEqualSelector((state) => getScrollToTop(state, timelineId));
useScrollToTop('#scrollableNotes', !!scrollToTop);

const timeline = useSelector((state: State) => selectTimelineById(state, timelineId));
const timelineSavedObjectId = useMemo(() => timeline?.savedObjectId ?? '', [timeline]);
const timeline: TimelineModel = useSelector((state: State) =>
selectTimelineById(state, timelineId)
);
const timelineSavedObjectId = useMemo(
() => timeline.savedObjectId ?? '',
[timeline.savedObjectId]
);
const isTimelineSaved: boolean = useMemo(
() => timelineSavedObjectId.length > 0,
[timelineSavedObjectId]
() => timeline.status === TimelineStatusEnum.active,
[timeline.status]
);

const fetchNotes = useCallback(
Expand Down

0 comments on commit f7b808c

Please sign in to comment.