diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_block.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_block.test.tsx
new file mode 100644
index 00000000000000..ebe68d31f0e603
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_block.test.tsx
@@ -0,0 +1,27 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { render } from '@testing-library/react';
+import { AlertHeaderBlock } from './alert_header_block';
+
+const title =
{'title'}
;
+const children = {'children'}
;
+const dataTestSubj = 'TITLE_TEST_ID';
+
+describe('', () => {
+ it('should render component', () => {
+ const { getByTestId } = render(
+
+ {children}
+
+ );
+
+ expect(getByTestId('TITLE_TEST_ID')).toHaveTextContent('title');
+ expect(getByTestId('CHILDREN_TEST_ID')).toBeInTheDocument();
+ });
+});
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_block.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_block.tsx
new file mode 100644
index 00000000000000..84259ffcf0eb62
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_block.tsx
@@ -0,0 +1,45 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import type { ReactElement, ReactNode, VFC } from 'react';
+import React, { memo } from 'react';
+import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiTitle } from '@elastic/eui';
+
+export interface AlertHeaderBlockProps {
+ /**
+ * React component to render as the title
+ */
+ title: ReactElement;
+ /**
+ * React component to render as the value
+ */
+ children: ReactNode;
+ /**
+ * data-test-subj to use for the title
+ */
+ ['data-test-subj']?: string;
+}
+
+/**
+ * Reusable component for rendering a block with rounded edges, show a title and value below one another
+ */
+export const AlertHeaderBlock: VFC = memo(
+ ({ title, children, 'data-test-subj': dataTestSubj }) => (
+
+
+
+
+ {title}
+
+
+ {children}
+
+
+ )
+);
+
+AlertHeaderBlock.displayName = 'AlertHeaderBlock';
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.test.tsx
index 4a0267a8dade84..6d72ca9e58dfac 100644
--- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.test.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.test.tsx
@@ -13,8 +13,10 @@ import {
SEVERITY_VALUE_TEST_ID,
FLYOUT_ALERT_HEADER_TITLE_TEST_ID,
STATUS_BUTTON_TEST_ID,
- ASSIGNEES_HEADER_TEST_ID,
ALERT_SUMMARY_PANEL_TEST_ID,
+ ASSIGNEES_TEST_ID,
+ ASSIGNEES_EMPTY_TEST_ID,
+ NOTES_TITLE_TEST_ID,
} from './test_ids';
import { AlertHeaderTitle } from './alert_header_title';
import moment from 'moment-timezone';
@@ -22,8 +24,10 @@ import { useDateFormat, useTimeZone } from '../../../../common/lib/kibana';
import { mockGetFieldsData } from '../../shared/mocks/mock_get_fields_data';
import { mockDataFormattedForFieldBrowser } from '../../shared/mocks/mock_data_formatted_for_field_browser';
import { TestProvidersComponent } from '../../../../common/mock';
+import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
jest.mock('../../../../common/lib/kibana');
+jest.mock('../../../../common/hooks/use_experimental_features');
moment.suppressDeprecationWarnings = true;
moment.tz.setDefault('UTC');
@@ -51,7 +55,7 @@ describe('', () => {
});
it('should render component', () => {
- const { getByTestId } = renderHeader(mockContextValue);
+ const { getByTestId, queryByTestId } = renderHeader(mockContextValue);
expect(getByTestId(HEADER_TEXT_TEST_ID)).toHaveTextContent('rule-name');
expect(getByTestId(SEVERITY_VALUE_TEST_ID)).toBeInTheDocument();
@@ -59,7 +63,8 @@ describe('', () => {
expect(getByTestId(RISK_SCORE_VALUE_TEST_ID)).toBeInTheDocument();
expect(getByTestId(STATUS_BUTTON_TEST_ID)).toBeInTheDocument();
- expect(getByTestId(ASSIGNEES_HEADER_TEST_ID)).toBeInTheDocument();
+ expect(getByTestId(ASSIGNEES_TEST_ID)).toBeInTheDocument();
+ expect(queryByTestId(ASSIGNEES_EMPTY_TEST_ID)).not.toBeInTheDocument();
});
it('should render title correctly if flyout is in preview', () => {
@@ -68,7 +73,15 @@ describe('', () => {
expect(getByTestId(RISK_SCORE_VALUE_TEST_ID)).toBeInTheDocument();
expect(queryByTestId(STATUS_BUTTON_TEST_ID)).not.toBeInTheDocument();
- expect(getByTestId(ASSIGNEES_HEADER_TEST_ID)).toHaveTextContent('Assignees—');
+ expect(getByTestId(ASSIGNEES_EMPTY_TEST_ID)).toBeInTheDocument();
+ expect(queryByTestId(ASSIGNEES_TEST_ID)).not.toBeInTheDocument();
+ });
+
+ it('should render notes section if experimental flag is enabled', () => {
+ (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
+
+ const { getByTestId } = renderHeader(mockContextValue);
+ expect(getByTestId(NOTES_TITLE_TEST_ID)).toBeInTheDocument();
});
it('should render fall back values if document is not alert', () => {
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.tsx
index a54a3a027fad18..f128fada5fb290 100644
--- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_header_title.tsx
@@ -6,11 +6,12 @@
*/
import React, { memo, useCallback, useMemo } from 'react';
-import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiPanel, useEuiTheme, EuiLink } from '@elastic/eui';
-import { css } from '@emotion/css';
+import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiLink } from '@elastic/eui';
import { ALERT_WORKFLOW_ASSIGNEE_IDS } from '@kbn/rule-data-utils';
import { i18n } from '@kbn/i18n';
import { FlyoutTitle } from '@kbn/security-solution-common';
+import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
+import { Notes } from './notes';
import { useRuleDetailsLink } from '../../shared/hooks/use_rule_details_link';
import { DocumentStatus } from './status';
import { DocumentSeverity } from './severity';
@@ -22,6 +23,11 @@ import { PreferenceFormattedDate } from '../../../../common/components/formatted
import { FLYOUT_ALERT_HEADER_TITLE_TEST_ID, ALERT_SUMMARY_PANEL_TEST_ID } from './test_ids';
import { Assignees } from './assignees';
+// minWidth for each block, allows to switch for a 1 row 4 blocks to 2 rows with 2 block each
+const blockStyles = {
+ minWidth: 280,
+};
+
/**
* Alert details flyout right section header
*/
@@ -34,10 +40,13 @@ export const AlertHeaderTitle = memo(() => {
refetchFlyoutData,
getFieldsData,
} = useDocumentDetailsContext();
+ const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled(
+ 'securitySolutionNotesEnabled'
+ );
+
const { isAlert, ruleName, timestamp, ruleId } = useBasicDataFromDetailsData(
dataFormattedForFieldBrowser
);
- const { euiTheme } = useEuiTheme();
const href = useRuleDetailsLink({ ruleId: !isPreview ? ruleId : null });
const ruleTitle = useMemo(
@@ -89,27 +98,52 @@ export const AlertHeaderTitle = memo(() => {
/>
)}
-
-
-
+ {securitySolutionNotesEnabled ? (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ) : (
+
+
-
+
@@ -121,7 +155,7 @@ export const AlertHeaderTitle = memo(() => {
/>
-
+ )}
>
);
});
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees.test.tsx
index 161fc4db117de4..3c27165f8cc115 100644
--- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees.test.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees.test.tsx
@@ -10,8 +10,8 @@ import { render } from '@testing-library/react';
import {
ASSIGNEES_ADD_BUTTON_TEST_ID,
+ ASSIGNEES_EMPTY_TEST_ID,
ASSIGNEES_TITLE_TEST_ID,
- ASSIGNEES_HEADER_TEST_ID,
} from './test_ids';
import { Assignees } from './assignees';
@@ -180,6 +180,6 @@ describe('', () => {
);
expect(queryByTestId(ASSIGNEES_ADD_BUTTON_TEST_ID)).not.toBeInTheDocument();
- expect(getByTestId(ASSIGNEES_HEADER_TEST_ID)).toHaveTextContent('Assignees—');
+ expect(getByTestId(ASSIGNEES_EMPTY_TEST_ID)).toHaveTextContent('—');
});
});
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees.tsx
index 140a7baab04ab9..aa8360c30d292d 100644
--- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/assignees.tsx
@@ -14,12 +14,12 @@ import {
EuiFlexGroup,
EuiFlexItem,
EuiPopover,
- EuiTitle,
EuiToolTip,
useGeneratedHtmlId,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
+import { AlertHeaderBlock } from './alert_header_block';
import { useSetAlertAssignees } from '../../../../common/components/toolbar/bulk_actions/use_set_alert_assignees';
import { getEmptyTagValue } from '../../../../common/components/empty_value';
import { ASSIGNEES_PANEL_WIDTH } from '../../../../common/components/assignees/constants';
@@ -32,7 +32,8 @@ import { useBulkGetUserProfiles } from '../../../../common/components/user_profi
import { UsersAvatarsPanel } from '../../../../common/components/user_profiles/users_avatars_panel';
import {
ASSIGNEES_ADD_BUTTON_TEST_ID,
- ASSIGNEES_HEADER_TEST_ID,
+ ASSIGNEES_EMPTY_TEST_ID,
+ ASSIGNEES_TEST_ID,
ASSIGNEES_TITLE_TEST_ID,
} from './test_ids';
@@ -158,26 +159,19 @@ export const Assignees: FC = memo(
]);
return (
-
+ }
+ data-test-subj={ASSIGNEES_TITLE_TEST_ID}
>
-
-
-
-
-
-
-
{isPreview ? (
- getEmptyTagValue()
+ {getEmptyTagValue()}
) : (
-
+
{assignedUsers && (
@@ -186,7 +180,7 @@ export const Assignees: FC = memo(
{updateAssigneesPopover}
)}
-
+
);
}
);
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/notes.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/notes.test.tsx
new file mode 100644
index 00000000000000..437fb5d9e21b24
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/notes.test.tsx
@@ -0,0 +1,154 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { render } from '@testing-library/react';
+import { DocumentDetailsContext } from '../../shared/context';
+import { NOTES_COUNT_TEST_ID, NOTES_LOADING_TEST_ID, NOTES_TITLE_TEST_ID } from './test_ids';
+import { FETCH_NOTES_ERROR, Notes } from './notes';
+import { mockContextValue } from '../../shared/mocks/mock_context';
+import { createMockStore, mockGlobalState, TestProviders } from '../../../../common/mock';
+import { ReqStatus } from '../../../../notes';
+import type { Note } from '../../../../../common/api/timeline';
+
+const mockAddError = jest.fn();
+jest.mock('../../../../common/hooks/use_app_toasts', () => ({
+ useAppToasts: () => ({
+ addError: mockAddError,
+ }),
+}));
+
+const mockDispatch = jest.fn();
+jest.mock('react-redux', () => {
+ const original = jest.requireActual('react-redux');
+ return {
+ ...original,
+ useDispatch: () => mockDispatch,
+ };
+});
+
+describe('', () => {
+ it('should render loading spinner', () => {
+ const store = createMockStore({
+ ...mockGlobalState,
+ notes: {
+ ...mockGlobalState.notes,
+ status: {
+ ...mockGlobalState.notes.status,
+ fetchNotesByDocumentIds: ReqStatus.Loading,
+ },
+ },
+ });
+
+ const { getByTestId } = render(
+
+
+
+
+
+ );
+
+ expect(mockDispatch).toHaveBeenCalled();
+ expect(getByTestId(NOTES_TITLE_TEST_ID)).toBeInTheDocument();
+ expect(getByTestId(NOTES_TITLE_TEST_ID)).toHaveTextContent('Notes');
+ expect(getByTestId(NOTES_LOADING_TEST_ID)).toBeInTheDocument();
+ });
+
+ it('should render number of notes', () => {
+ const contextValue = {
+ ...mockContextValue,
+ eventId: '1',
+ };
+
+ const { getByTestId } = render(
+
+
+
+
+
+ );
+
+ expect(getByTestId(NOTES_TITLE_TEST_ID)).toBeInTheDocument();
+ expect(getByTestId(NOTES_TITLE_TEST_ID)).toHaveTextContent('Notes');
+ expect(getByTestId(NOTES_COUNT_TEST_ID)).toBeInTheDocument();
+ expect(getByTestId(NOTES_COUNT_TEST_ID)).toHaveTextContent('1');
+ });
+
+ it('should render number of notes in scientific notation for big numbers', () => {
+ const createMockNote = (noteId: string): Note => ({
+ eventId: '1', // should be a valid id based on mockTimelineData
+ noteId,
+ note: 'note-1',
+ timelineId: 'timeline-1',
+ created: 1663882629000,
+ createdBy: 'elastic',
+ updated: 1663882629000,
+ updatedBy: 'elastic',
+ version: 'version',
+ });
+ const mockEntities = [...Array(1000).keys()]
+ .map((i: number) => createMockNote(i.toString()))
+ .reduce((acc, entity) => {
+ // @ts-ignore
+ acc[entity.noteId] = entity;
+ return acc;
+ }, {});
+ const mockIds = Object.keys(mockEntities);
+
+ const store = createMockStore({
+ ...mockGlobalState,
+ notes: {
+ ...mockGlobalState.notes,
+ entities: mockEntities,
+ ids: mockIds,
+ },
+ });
+ const contextValue = {
+ ...mockContextValue,
+ eventId: '1',
+ };
+
+ const { getByTestId } = render(
+
+
+
+
+
+ );
+
+ expect(getByTestId(NOTES_COUNT_TEST_ID)).toHaveTextContent('1k');
+ });
+
+ it('should render toast error', () => {
+ const store = createMockStore({
+ ...mockGlobalState,
+ notes: {
+ ...mockGlobalState.notes,
+ status: {
+ ...mockGlobalState.notes.status,
+ fetchNotesByDocumentIds: ReqStatus.Failed,
+ },
+ error: {
+ ...mockGlobalState.notes.error,
+ fetchNotesByDocumentIds: { type: 'http', status: 500 },
+ },
+ },
+ });
+
+ render(
+
+
+
+
+
+ );
+
+ expect(mockAddError).toHaveBeenCalledWith(null, {
+ title: FETCH_NOTES_ERROR,
+ });
+ });
+});
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/notes.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/notes.tsx
new file mode 100644
index 00000000000000..41f96c90f89ec4
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/notes.tsx
@@ -0,0 +1,87 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { memo, useEffect } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import { FormattedMessage } from '@kbn/i18n-react';
+import { i18n } from '@kbn/i18n';
+import { EuiLoadingSpinner } from '@elastic/eui';
+import { FormattedCount } from '../../../../common/components/formatted_number';
+import { useDocumentDetailsContext } from '../../shared/context';
+import { NOTES_COUNT_TEST_ID, NOTES_LOADING_TEST_ID, NOTES_TITLE_TEST_ID } from './test_ids';
+import type { State } from '../../../../common/store';
+import type { Note } from '../../../../../common/api/timeline';
+import {
+ fetchNotesByDocumentIds,
+ ReqStatus,
+ selectFetchNotesByDocumentIdsError,
+ selectFetchNotesByDocumentIdsStatus,
+ selectSortedNotesByDocumentId,
+} from '../../../../notes/store/notes.slice';
+import { useAppToasts } from '../../../../common/hooks/use_app_toasts';
+import { AlertHeaderBlock } from './alert_header_block';
+
+export const FETCH_NOTES_ERROR = i18n.translate(
+ 'xpack.securitySolution.notes.fetchNotesErrorLabel',
+ {
+ defaultMessage: 'Error fetching notes',
+ }
+);
+
+/**
+ * Renders a block with the number of notes for the event
+ */
+export const Notes = memo(() => {
+ const dispatch = useDispatch();
+ const { eventId } = useDocumentDetailsContext();
+ const { addError: addErrorToast } = useAppToasts();
+
+ useEffect(() => {
+ dispatch(fetchNotesByDocumentIds({ documentIds: [eventId] }));
+ }, [dispatch, eventId]);
+
+ const fetchStatus = useSelector((state: State) => selectFetchNotesByDocumentIdsStatus(state));
+ const fetchError = useSelector((state: State) => selectFetchNotesByDocumentIdsError(state));
+
+ const notes: Note[] = useSelector((state: State) =>
+ selectSortedNotesByDocumentId(state, {
+ documentId: eventId,
+ sort: { field: 'created', direction: 'desc' },
+ })
+ );
+
+ // show a toast if the fetch notes call fails
+ useEffect(() => {
+ if (fetchStatus === ReqStatus.Failed && fetchError) {
+ addErrorToast(null, {
+ title: FETCH_NOTES_ERROR,
+ });
+ }
+ }, [addErrorToast, fetchError, fetchStatus]);
+
+ return (
+
+ }
+ data-test-subj={NOTES_TITLE_TEST_ID}
+ >
+ {fetchStatus === ReqStatus.Loading ? (
+
+ ) : (
+
+
+
+ )}
+
+ );
+});
+
+Notes.displayName = 'Notes';
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/risk_score.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/risk_score.tsx
index dfa03cabd53e66..fba4343e974998 100644
--- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/risk_score.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/risk_score.tsx
@@ -6,9 +6,9 @@
*/
import React, { memo } from 'react';
-import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
import { ALERT_RISK_SCORE } from '@kbn/rule-data-utils';
import { FormattedMessage } from '@kbn/i18n-react';
+import { AlertHeaderBlock } from './alert_header_block';
import { RISK_SCORE_TITLE_TEST_ID, RISK_SCORE_VALUE_TEST_ID } from './test_ids';
import { useDocumentDetailsContext } from '../../shared/context';
@@ -33,21 +33,17 @@ export const RiskScore = memo(() => {
}
return (
-
-
-
-
-
-
-
-
-
- {alertRiskScore}
-
-
+
+ }
+ data-test-subj={RISK_SCORE_TITLE_TEST_ID}
+ >
+ {alertRiskScore}
+
);
});
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/status.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/status.tsx
index 420eb7feafa4a3..601201f2e58e81 100644
--- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/status.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/status.tsx
@@ -9,7 +9,7 @@ import type { FC } from 'react';
import React, { useMemo } from 'react';
import { find } from 'lodash/fp';
import { FormattedMessage } from '@kbn/i18n-react';
-import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
+import { AlertHeaderBlock } from './alert_header_block';
import { getEmptyTagValue } from '../../../../common/components/empty_value';
import { SIGNAL_STATUS_FIELD_NAME } from '../../../../timelines/components/timeline/body/renderers/constants';
import { StatusPopoverButton } from './status_popover_button';
@@ -51,32 +51,28 @@ export const DocumentStatus: FC = () => {
}, [browserFields, dataFormattedForFieldBrowser, eventId, scopeId]);
return (
-
-
-
-
-
-
-
-
-
- {!statusData || !hasData(statusData) || isPreview ? (
- getEmptyTagValue()
- ) : (
-
-
-
- )}
-
-
+
+ }
+ data-test-subj={STATUS_TITLE_TEST_ID}
+ >
+ {!statusData || !hasData(statusData) || isPreview ? (
+ getEmptyTagValue()
+ ) : (
+
+
+
+ )}
+
);
};
diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts
index d215f161724148..83f833a4b24ace 100644
--- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts
+++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts
@@ -33,7 +33,12 @@ export const RISK_SCORE_VALUE_TEST_ID = `${FLYOUT_HEADER_TEST_ID}RiskScoreValue`
export const SHARE_BUTTON_TEST_ID = `${FLYOUT_HEADER_TEST_ID}ShareButton` as const;
export const CHAT_BUTTON_TEST_ID = 'newChatByTitle' as const;
-export const ASSIGNEES_HEADER_TEST_ID = `${FLYOUT_HEADER_TEST_ID}AssigneesHeader` as const;
+export const NOTES_TITLE_TEST_ID = `${FLYOUT_HEADER_TEST_ID}NotesTitle` as const;
+export const NOTES_COUNT_TEST_ID = `${FLYOUT_HEADER_TEST_ID}NotesCount` as const;
+export const NOTES_LOADING_TEST_ID = `${FLYOUT_HEADER_TEST_ID}NotesLoading` as const;
+
+export const ASSIGNEES_EMPTY_TEST_ID = `${FLYOUT_HEADER_TEST_ID}AssigneesEmpty` as const;
+export const ASSIGNEES_TEST_ID = `${FLYOUT_HEADER_TEST_ID}Assignees` as const;
export const ASSIGNEES_TITLE_TEST_ID = `${FLYOUT_HEADER_TEST_ID}AssigneesTitle` as const;
export const ASSIGNEES_ADD_BUTTON_TEST_ID = `${FLYOUT_HEADER_TEST_ID}AssigneesAddButton` as const;
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel.cy.ts
index 2d0d80af9c2426..ab8f5557a5fafc 100644
--- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel.cy.ts
+++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_right_panel.cy.ts
@@ -34,7 +34,6 @@ import {
DOCUMENT_DETAILS_FLYOUT_HEADER_LINK_ICON,
DOCUMENT_DETAILS_FLYOUT_HEADER_RISK_SCORE,
DOCUMENT_DETAILS_FLYOUT_HEADER_RISK_SCORE_VALUE,
- DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES,
DOCUMENT_DETAILS_FLYOUT_HEADER_SEVERITY_VALUE,
DOCUMENT_DETAILS_FLYOUT_HEADER_STATUS,
DOCUMENT_DETAILS_FLYOUT_HEADER_TITLE,
@@ -42,6 +41,7 @@ import {
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB,
DOCUMENT_DETAILS_FLYOUT_TABLE_TAB,
DOCUMENT_DETAILS_FLYOUT_FOOTER_ISOLATE_HOST,
+ DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES_TITLE,
} from '../../../../screens/expandable_flyout/alert_details_right_panel';
import {
closeFlyout,
@@ -93,7 +93,11 @@ describe('Alert details expandable flyout right panel', { tags: ['@ess', '@serve
.should('be.visible')
.and('have.text', rule.risk_score);
- cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES).should('have.text', 'Assignees');
+ cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES_TITLE).should('have.text', 'Assignees');
+
+ // TODO uncomment when the securitySolutionNotesEnabled feature flag is removed
+ // cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_NOTES_TITLE).should('have.text', 'Notes');
+ // cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_NOTES_VALUE).should('have.text', '0');
cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_SEVERITY_VALUE)
.should('be.visible')
diff --git a/x-pack/test/security_solution_cypress/cypress/screens/expandable_flyout/alert_details_right_panel.ts b/x-pack/test/security_solution_cypress/cypress/screens/expandable_flyout/alert_details_right_panel.ts
index 41cd41eb83890e..e84a70cbea621c 100644
--- a/x-pack/test/security_solution_cypress/cypress/screens/expandable_flyout/alert_details_right_panel.ts
+++ b/x-pack/test/security_solution_cypress/cypress/screens/expandable_flyout/alert_details_right_panel.ts
@@ -48,8 +48,17 @@ export const DOCUMENT_DETAILS_FLYOUT_HEADER_RISK_SCORE_VALUE = getDataTestSubjec
'securitySolutionFlyoutHeaderRiskScoreValue'
);
export const DOCUMENT_DETAILS_FLYOUT_HEADER_SEVERITY_VALUE = getDataTestSubjectSelector('severity');
-export const DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES = getDataTestSubjectSelector(
- 'securitySolutionFlyoutHeaderAssigneesHeader'
+export const DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES_TITLE = getDataTestSubjectSelector(
+ 'securitySolutionFlyoutHeaderAssigneesTitle'
+);
+export const DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES_VALUE = getDataTestSubjectSelector(
+ 'securitySolutionFlyoutHeaderAssignees'
+);
+export const DOCUMENT_DETAILS_FLYOUT_HEADER_NOTES_TITLE = getDataTestSubjectSelector(
+ 'securitySolutionFlyoutHeaderAssigneesTitle'
+);
+export const DOCUMENT_DETAILS_FLYOUT_HEADER_NOTES_VALUE = getDataTestSubjectSelector(
+ 'securitySolutionFlyoutHeaderAssigneesValue'
);
/* Footer */
diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/alert_assignments.ts b/x-pack/test/security_solution_cypress/cypress/tasks/alert_assignments.ts
index 460b9bf14ea331..eb2f81b84071af 100644
--- a/x-pack/test/security_solution_cypress/cypress/tasks/alert_assignments.ts
+++ b/x-pack/test/security_solution_cypress/cypress/tasks/alert_assignments.ts
@@ -27,7 +27,7 @@ import {
ALERT_ASSIGNEES_SELECTABLE_OPTIONS,
} from '../screens/alerts';
import { PAGE_TITLE } from '../screens/common/page';
-import { DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES } from '../screens/expandable_flyout/alert_details_right_panel';
+import { DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES_VALUE } from '../screens/expandable_flyout/alert_details_right_panel';
import { expandFirstAlertActions, selectFirstPageAlerts } from './alerts';
import { login } from './login';
import { visitWithTimeRange } from './navigation';
@@ -85,7 +85,7 @@ export const checkEmptyAssigneesStateInAlertsTable = () => {
};
export const checkEmptyAssigneesStateInAlertDetailsFlyout = () => {
- cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES).within(() => {
+ cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES_VALUE).within(() => {
cy.get(ALERT_AVATARS_PANEL).children().should('have.length', 0);
});
};
@@ -137,13 +137,13 @@ export const alertsTableShowsAssigneesBadgeForFirstAlert = (users: string[]) =>
};
export const alertDetailsFlyoutShowsAssignees = (users: string[]) => {
- cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES).within(() => {
+ cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES_VALUE).within(() => {
users.forEach((user) => cy.get(`.euiAvatar${ALERT_USER_AVATAR(user)}`).should('exist'));
});
};
export const alertDetailsFlyoutShowsAssigneesBadge = (users: string[]) => {
- cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES).within(() => {
+ cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_ASSIGNEES_VALUE).within(() => {
cy.get(ALERT_ASSIGNEES_COUNT_BADGE).contains(users.length);
users.forEach((user) => cy.get(`.euiAvatar${ALERT_USER_AVATAR(user)}`).should('not.exist'));
});