Skip to content

Commit

Permalink
fix note pinning
Browse files Browse the repository at this point in the history
  • Loading branch information
christineweng committed Oct 1, 2024
1 parent c7e44c8 commit 2d8e711
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import { EuiButtonIcon, EuiToolTip } from '@elastic/eui';
import styled from 'styled-components';

import { TimelineTabs, TableId } from '@kbn/securitysolution-data-table';
import { selectNotesByDocumentId } from '../../../notes/store/notes.slice';
import {
selectNotesByDocumentId,
selectDocumentNotesBySavedObjectId,
} from '../../../notes/store/notes.slice';
import type { State } from '../../store';
import { selectTimelineById } from '../../../timelines/store/selectors';
import {
Expand Down Expand Up @@ -70,7 +73,7 @@ const ActionsComponent: React.FC<ActionProps> = ({
}) => {
const dispatch = useDispatch();

const { timelineType } = useShallowEqualSelector((state) =>
const { timelineType, savedObjectId } = useShallowEqualSelector((state) =>
isTimelineScope(timelineId) ? selectTimelineById(state, timelineId) : timelineDefaults
);

Expand Down Expand Up @@ -222,6 +225,12 @@ const ActionsComponent: React.FC<ActionProps> = ({

/* only applicable for new event based notes */
const documentBasedNotes = useSelector((state: State) => selectNotesByDocumentId(state, eventId));
const documentBasedNotesInTimeline = useSelector((state: State) =>
selectDocumentNotesBySavedObjectId(state, {
documentId: eventId,
savedObjectId: savedObjectId ?? '',
})
);

/* only applicable notes before event based notes */
const timelineNoteIds = useMemo(
Expand All @@ -234,11 +243,14 @@ const ActionsComponent: React.FC<ActionProps> = ({
[documentBasedNotes, timelineNoteIds, securitySolutionNotesEnabled]
);

const noteIds = useMemo(() => {
return securitySolutionNotesEnabled
? documentBasedNotes.map((note) => note.noteId)
: timelineNoteIds;
}, [documentBasedNotes, timelineNoteIds, securitySolutionNotesEnabled]);
/* note ids specific to the current timeline, it is used to enable/disable pinning */
const noteIdsInTimeline = useMemo(() => {
if (securitySolutionNotesEnabled) {
// if timeline is unsaved, there is no notes associated to timeline yet
return savedObjectId ? documentBasedNotesInTimeline.map((note) => note.noteId) : [];
}
return timelineNoteIds;
}, [documentBasedNotesInTimeline, timelineNoteIds, securitySolutionNotesEnabled, savedObjectId]);

return (
<ActionsContainer data-test-subj="actions-container">
Expand Down Expand Up @@ -291,7 +303,7 @@ const ActionsComponent: React.FC<ActionProps> = ({
isAlert={isAlert(eventType)}
key="pin-event"
onPinClicked={handlePinClicked}
noteIds={noteIds}
noteIds={noteIdsInTimeline}
eventIsPinned={isEventPinned}
timelineType={timelineType}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import { useKibana } from '../../common/lib/kibana';
import { ADD_NOTE_BUTTON_TEST_ID, ADD_NOTE_MARKDOWN_TEST_ID } from './test_ids';
import { useAppToasts } from '../../common/hooks/use_app_toasts';
import type { State } from '../../common/store';
import { timelineSelectors } from '../../timelines/store';
import { TimelineId } from '../../../common/types';
import { pinEvent } from '../../timelines/store/actions';
import {
createNote,
ReqStatus,
Expand Down Expand Up @@ -77,6 +80,9 @@ export const AddNote = memo(

const createStatus = useSelector((state: State) => selectCreateNoteStatus(state));
const createError = useSelector((state: State) => selectCreateNoteError(state));
const activeTimeline = useSelector((state: State) =>
timelineSelectors.selectTimelineById(state, TimelineId.active)
);

const addNote = useCallback(() => {
dispatch(
Expand All @@ -88,11 +94,22 @@ export const AddNote = memo(
},
})
);

// Automatically pin an associated event if it's attached to a timeline and it's not pinned yet
const isEventPinned = eventId ? activeTimeline.pinnedEventIds[eventId] === true : false;
if (!isEventPinned && eventId && timelineId) {
dispatch(
pinEvent({
id: TimelineId.active,
eventId,
})
);
}
telemetry.reportAddNoteFromExpandableFlyoutClicked({
isRelatedToATimeline: timelineId != null,
});
setEditorValue('');
}, [dispatch, editorValue, eventId, telemetry, timelineId]);
}, [dispatch, editorValue, eventId, telemetry, timelineId, activeTimeline.pinnedEventIds]);

// show a toast if the create note call fails
useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
selectNoteById,
selectNoteIds,
selectNotesByDocumentId,
selectDocumentNotesBySavedObjectId,
selectNotesPagination,
selectNotesTablePendingDeleteIds,
selectNotesTableSearch,
Expand Down Expand Up @@ -608,6 +609,21 @@ describe('notesSlice', () => {
expect(selectNotesByDocumentId(mockGlobalState, 'wrong-document-id')).toHaveLength(0);
});

it('should return no notes if no notes is found with specified document id and saved object id', () => {
expect(
selectDocumentNotesBySavedObjectId(mockGlobalState, {
documentId: '1',
savedObjectId: 'wrong-savedObjectId',
})
).toHaveLength(0);
expect(
selectDocumentNotesBySavedObjectId(mockGlobalState, {
documentId: 'wrong-document-id',
savedObjectId: 'some-timeline-id',
})
).toHaveLength(0);
});

it('should return all notes sorted for an existing document id', () => {
const oldestNote = {
eventId: '1', // should be a valid id based on mockTimelineData
Expand Down
12 changes: 12 additions & 0 deletions x-pack/plugins/security_solution/public/notes/store/notes.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,18 @@ export const selectNotesBySavedObjectId = createSelector(
savedObjectId.length > 0 ? notes.filter((note) => note.timelineId === savedObjectId) : []
);

export const selectDocumentNotesBySavedObjectId = createSelector(
[
selectAllNotes,
(
state: State,
{ documentId, savedObjectId }: { documentId: string; savedObjectId: string }
) => ({ documentId, savedObjectId }),
],
(notes, { documentId, savedObjectId }) =>
notes.filter((note) => note.eventId === documentId && note.timelineId === savedObjectId)
);

export const selectSortedNotesByDocumentId = createSelector(
[
selectAllNotes,
Expand Down

0 comments on commit 2d8e711

Please sign in to comment.