diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/notes_list.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/notes_list.tsx index 24999af0a89d10..2eb18cea3c04b2 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/notes_list.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/notes_list.tsx @@ -37,7 +37,7 @@ import { selectDeleteNotesStatus, selectFetchNotesByDocumentIdsError, selectFetchNotesByDocumentIdsStatus, - selectNotesByDocumentId, + selectSortedNotesByDocumentId, } from '../../../../notes/store/notes.slice'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; import { useUserPrivileges } from '../../../../common/components/user_privileges'; @@ -90,7 +90,12 @@ export const NotesList = memo(({ eventId }: NotesListProps) => { const fetchStatus = useSelector((state: State) => selectFetchNotesByDocumentIdsStatus(state)); const fetchError = useSelector((state: State) => selectFetchNotesByDocumentIdsError(state)); - const notes: Note[] = useSelector((state: State) => selectNotesByDocumentId(state, eventId)); + const notes: Note[] = useSelector((state: State) => + selectSortedNotesByDocumentId(state, { + documentId: eventId, + sort: { field: 'created', direction: 'desc' }, + }) + ); const createStatus = useSelector((state: State) => selectCreateNoteStatus(state)); diff --git a/x-pack/plugins/security_solution/public/notes/store/notes.slice.test.ts b/x-pack/plugins/security_solution/public/notes/store/notes.slice.test.ts index ad0e3b198d0d91..5825a170bf1cfb 100644 --- a/x-pack/plugins/security_solution/public/notes/store/notes.slice.test.ts +++ b/x-pack/plugins/security_solution/public/notes/store/notes.slice.test.ts @@ -41,6 +41,7 @@ import { userSelectedRow, userSelectedRowForDeletion, userSortedNotes, + selectSortedNotesByDocumentId, } from './notes.slice'; import type { NotesState } from './notes.slice'; import { mockGlobalState } from '../../common/mock'; @@ -515,6 +516,63 @@ describe('notesSlice', () => { expect(selectNotesByDocumentId(mockGlobalState, 'wrong-document-id')).toHaveLength(0); }); + it('should return all notes sorted dor an existing document id', () => { + const oldestNote = { + eventId: '1', // should be a valid id based on mockTimelineData + noteId: '1', + note: 'note-1', + timelineId: 'timeline-1', + created: 1663882629000, + createdBy: 'elastic', + updated: 1663882629000, + updatedBy: 'elastic', + version: 'version', + }; + const newestNote = { + ...oldestNote, + noteId: '2', + created: 1663882689000, + }; + + const state = { + ...mockGlobalState, + notes: { + ...mockGlobalState.notes, + entities: { + '1': oldestNote, + '2': newestNote, + }, + ids: ['1', '2'], + }, + }; + + const ascResult = selectSortedNotesByDocumentId(state, { + documentId: '1', + sort: { field: 'created', direction: 'asc' }, + }); + expect(ascResult[0]).toEqual(oldestNote); + expect(ascResult[1]).toEqual(newestNote); + + const descResult = selectSortedNotesByDocumentId(state, { + documentId: '1', + sort: { field: 'created', direction: 'desc' }, + }); + expect(descResult[0]).toEqual(newestNote); + expect(descResult[1]).toEqual(oldestNote); + }); + + it('should also return no notes if document id does not exist', () => { + expect( + selectSortedNotesByDocumentId(mockGlobalState, { + documentId: 'wrong-document-id', + sort: { + field: 'created', + direction: 'desc', + }, + }) + ).toHaveLength(0); + }); + it('should select notes pagination', () => { const state = { ...mockGlobalState, diff --git a/x-pack/plugins/security_solution/public/notes/store/notes.slice.ts b/x-pack/plugins/security_solution/public/notes/store/notes.slice.ts index 3b59c8be957c35..4f333103a2a254 100644 --- a/x-pack/plugins/security_solution/public/notes/store/notes.slice.ts +++ b/x-pack/plugins/security_solution/public/notes/store/notes.slice.ts @@ -276,10 +276,35 @@ export const selectFetchNotesError = (state: State) => state.notes.error.fetchNo export const selectFetchNotesStatus = (state: State) => state.notes.status.fetchNotes; export const selectNotesByDocumentId = createSelector( - [selectAllNotes, (state, documentId) => documentId], + [selectAllNotes, (state: State, documentId: string) => documentId], (notes, documentId) => notes.filter((note) => note.eventId === documentId) ); +export const selectSortedNotesByDocumentId = createSelector( + [ + selectAllNotes, + ( + state: State, + { + documentId, + sort, + }: { documentId: string; sort: { field: keyof Note; direction: 'asc' | 'desc' } } + ) => ({ documentId, sort }), + ], + (notes, { documentId, sort }) => { + const { field, direction } = sort; + return notes + .filter((note: Note) => note.eventId === documentId) + .sort((first: Note, second: Note) => { + const a = first[field]; + const b = second[field]; + if (a == null) return 1; + if (b == null) return -1; + return direction === 'asc' ? (a > b ? 1 : -1) : a > b ? -1 : 1; + }); + } +); + export const { userSelectedPage, userSelectedPerPage,