mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Security Solution] - remove expandableFlyoutDisabled feature flag (#187759)
This commit is contained in:
parent
4b9a9d704c
commit
1f82d5d68c
31 changed files with 340 additions and 1649 deletions
|
@ -105,11 +105,6 @@ export const allowedExperimentalValues = Object.freeze({
|
|||
*/
|
||||
alertTypeEnabled: false,
|
||||
|
||||
/**
|
||||
* Disables expandable flyout
|
||||
*/
|
||||
expandableFlyoutDisabled: false,
|
||||
|
||||
/**
|
||||
* Enables new notes
|
||||
*/
|
||||
|
@ -132,7 +127,6 @@ export const allowedExperimentalValues = Object.freeze({
|
|||
|
||||
/**
|
||||
* Enables the Managed User section inside the new user details flyout.
|
||||
* To see this section you also need expandableFlyoutDisabled flag set to false.
|
||||
*/
|
||||
newUserDetailsFlyoutManagedUser: false,
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ import type { CaseViewRefreshPropInterface } from '@kbn/cases-plugin/common';
|
|||
import { CaseMetricsFeature } from '@kbn/cases-plugin/common';
|
||||
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
|
||||
import { CaseDetailsRefreshContext } from '../../common/components/endpoint';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features';
|
||||
import { DocumentDetailsRightPanelKey } from '../../flyout/document_details/shared/constants/panel_keys';
|
||||
import { useTourContext } from '../../common/components/guided_onboarding_tour';
|
||||
import {
|
||||
|
@ -58,7 +57,6 @@ const CaseContainerComponent: React.FC = () => {
|
|||
SecurityPageName.rules
|
||||
);
|
||||
const { openFlyout } = useExpandableFlyoutApi();
|
||||
const expandableFlyoutDisabled = useIsExperimentalFeatureEnabled('expandableFlyoutDisabled');
|
||||
|
||||
const getDetectionsRuleDetailsHref = useCallback(
|
||||
(ruleId) => detectionsFormatUrl(getRuleDetailsUrl(ruleId ?? '', detectionsUrlSearch)),
|
||||
|
@ -69,38 +67,22 @@ const CaseContainerComponent: React.FC = () => {
|
|||
|
||||
const showAlertDetails = useCallback(
|
||||
(alertId: string, index: string) => {
|
||||
if (!expandableFlyoutDisabled) {
|
||||
openFlyout({
|
||||
right: {
|
||||
id: DocumentDetailsRightPanelKey,
|
||||
params: {
|
||||
id: alertId,
|
||||
indexName: index,
|
||||
scopeId: TimelineId.casePage,
|
||||
},
|
||||
openFlyout({
|
||||
right: {
|
||||
id: DocumentDetailsRightPanelKey,
|
||||
params: {
|
||||
id: alertId,
|
||||
indexName: index,
|
||||
scopeId: TimelineId.casePage,
|
||||
},
|
||||
});
|
||||
telemetry.reportDetailsFlyoutOpened({
|
||||
location: TimelineId.casePage,
|
||||
panel: 'right',
|
||||
});
|
||||
}
|
||||
// TODO remove when https://github.com/elastic/security-team/issues/7462 is merged
|
||||
// support of old flyout in cases page
|
||||
else {
|
||||
dispatch(
|
||||
timelineActions.toggleDetailPanel({
|
||||
panelView: 'eventDetail',
|
||||
id: TimelineId.casePage,
|
||||
params: {
|
||||
eventId: alertId,
|
||||
indexName: index,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
telemetry.reportDetailsFlyoutOpened({
|
||||
location: TimelineId.casePage,
|
||||
panel: 'right',
|
||||
});
|
||||
},
|
||||
[dispatch, expandableFlyoutDisabled, openFlyout, telemetry]
|
||||
[openFlyout, telemetry]
|
||||
);
|
||||
|
||||
const endpointDetailsHref = (endpointId: string) =>
|
||||
|
|
|
@ -7,14 +7,9 @@
|
|||
|
||||
import type { EuiDataGridCellValueElementProps } from '@elastic/eui';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
|
||||
import { dataTableActions, TableId } from '@kbn/securitysolution-data-table';
|
||||
import { LeftPanelNotesTab } from '../../../../flyout/document_details/left';
|
||||
import { useRouteSpy } from '../../../utils/route/use_route_spy';
|
||||
import { useKibana } from '../../../lib/kibana';
|
||||
import { timelineActions } from '../../../../timelines/store';
|
||||
import { SecurityPageName } from '../../../../../common/constants';
|
||||
import {
|
||||
DocumentDetailsLeftPanelKey,
|
||||
DocumentDetailsRightPanelKey,
|
||||
|
@ -23,12 +18,10 @@ import type {
|
|||
SetEventsDeleted,
|
||||
SetEventsLoading,
|
||||
ControlColumnProps,
|
||||
ExpandedDetailType,
|
||||
} from '../../../../../common/types';
|
||||
import { getMappedNonEcsValue } from '../../../../timelines/components/timeline/body/data_driven_columns';
|
||||
import type { TimelineItem, TimelineNonEcsData } from '../../../../../common/search_strategy';
|
||||
import type { ColumnHeaderOptions, OnRowSelected } from '../../../../../common/types/timeline';
|
||||
import { TimelineId } from '../../../../../common/types';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../hooks/use_experimental_features';
|
||||
import { useTourContext } from '../../guided_onboarding_tour';
|
||||
import { AlertsCasesTourSteps, SecurityStepId } from '../../guided_onboarding_tour/tour_config';
|
||||
|
@ -79,8 +72,6 @@ const RowActionComponent = ({
|
|||
const { telemetry } = useKibana().services;
|
||||
const { openFlyout } = useExpandableFlyoutApi();
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const [{ pageName }] = useRouteSpy();
|
||||
const { activeStep, isTourShown } = useTourContext();
|
||||
const shouldFocusOnOverviewTab =
|
||||
(activeStep === AlertsCasesTourSteps.expandEvent ||
|
||||
|
@ -102,72 +93,27 @@ const RowActionComponent = ({
|
|||
[columnHeaders, timelineNonEcsData]
|
||||
);
|
||||
|
||||
// TODO remove when https://github.com/elastic/security-team/issues/7462 is merged
|
||||
const expandableFlyoutDisabled = useIsExperimentalFeatureEnabled('expandableFlyoutDisabled');
|
||||
const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled(
|
||||
'securitySolutionNotesEnabled'
|
||||
);
|
||||
const showExpandableFlyout =
|
||||
pageName === SecurityPageName.attackDiscovery ? true : !expandableFlyoutDisabled;
|
||||
|
||||
const handleOnEventDetailPanelOpened = useCallback(() => {
|
||||
const updatedExpandedDetail: ExpandedDetailType = {
|
||||
panelView: 'eventDetail',
|
||||
params: {
|
||||
eventId: eventId ?? '',
|
||||
indexName: indexName ?? '',
|
||||
},
|
||||
};
|
||||
|
||||
if (showExpandableFlyout) {
|
||||
openFlyout({
|
||||
right: {
|
||||
id: DocumentDetailsRightPanelKey,
|
||||
path: shouldFocusOnOverviewTab ? { tab: 'overview' } : undefined,
|
||||
params: {
|
||||
id: eventId,
|
||||
indexName,
|
||||
scopeId: tableId,
|
||||
},
|
||||
openFlyout({
|
||||
right: {
|
||||
id: DocumentDetailsRightPanelKey,
|
||||
path: shouldFocusOnOverviewTab ? { tab: 'overview' } : undefined,
|
||||
params: {
|
||||
id: eventId,
|
||||
indexName,
|
||||
scopeId: tableId,
|
||||
},
|
||||
});
|
||||
telemetry.reportDetailsFlyoutOpened({
|
||||
location: tableId,
|
||||
panel: 'right',
|
||||
});
|
||||
}
|
||||
// TODO remove when https://github.com/elastic/security-team/issues/7462 is merged
|
||||
// support of old flyout in cases page
|
||||
else if (tableId === TableId.alertsOnCasePage) {
|
||||
dispatch(
|
||||
timelineActions.toggleDetailPanel({
|
||||
...updatedExpandedDetail,
|
||||
id: TimelineId.casePage,
|
||||
})
|
||||
);
|
||||
}
|
||||
// TODO remove when https://github.com/elastic/security-team/issues/7462 is merged
|
||||
// support of old flyout
|
||||
else {
|
||||
dispatch(
|
||||
dataTableActions.toggleDetailPanel({
|
||||
...updatedExpandedDetail,
|
||||
tabType,
|
||||
id: tableId,
|
||||
})
|
||||
);
|
||||
}
|
||||
}, [
|
||||
eventId,
|
||||
indexName,
|
||||
showExpandableFlyout,
|
||||
tableId,
|
||||
openFlyout,
|
||||
shouldFocusOnOverviewTab,
|
||||
telemetry,
|
||||
dispatch,
|
||||
tabType,
|
||||
]);
|
||||
},
|
||||
});
|
||||
telemetry.reportDetailsFlyoutOpened({
|
||||
location: tableId,
|
||||
panel: 'right',
|
||||
});
|
||||
}, [eventId, indexName, tableId, openFlyout, shouldFocusOnOverviewTab, telemetry]);
|
||||
|
||||
const toggleShowNotes = useCallback(() => {
|
||||
openFlyout({
|
||||
|
@ -229,14 +175,12 @@ const RowActionComponent = ({
|
|||
showCheckboxes={showCheckboxes}
|
||||
tabType={tabType}
|
||||
timelineId={tableId}
|
||||
toggleShowNotes={
|
||||
!expandableFlyoutDisabled && securitySolutionNotesEnabled ? toggleShowNotes : undefined
|
||||
}
|
||||
toggleShowNotes={securitySolutionNotesEnabled ? toggleShowNotes : undefined}
|
||||
width={width}
|
||||
setEventsLoading={setEventsLoading}
|
||||
setEventsDeleted={setEventsDeleted}
|
||||
refetch={refetch}
|
||||
showNotes={!expandableFlyoutDisabled && securitySolutionNotesEnabled ? true : false}
|
||||
showNotes={securitySolutionNotesEnabled ? true : false}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
|
|
@ -220,8 +220,6 @@ const ActionsComponent: React.FC<ActionProps> = ({
|
|||
'securitySolutionNotesEnabled'
|
||||
);
|
||||
|
||||
const expandableFlyoutDisabled = useIsExperimentalFeatureEnabled('expandableFlyoutDisabled');
|
||||
|
||||
/* only applicable for new event based notes */
|
||||
const documentBasedNotes = useSelector((state: State) => selectNotesByDocumentId(state, eventId));
|
||||
|
||||
|
@ -232,18 +230,15 @@ const ActionsComponent: React.FC<ActionProps> = ({
|
|||
);
|
||||
|
||||
const notesCount = useMemo(
|
||||
() =>
|
||||
securitySolutionNotesEnabled && !expandableFlyoutDisabled
|
||||
? documentBasedNotes.length
|
||||
: timelineNoteIds.length,
|
||||
[documentBasedNotes, timelineNoteIds, securitySolutionNotesEnabled, expandableFlyoutDisabled]
|
||||
() => (securitySolutionNotesEnabled ? documentBasedNotes.length : timelineNoteIds.length),
|
||||
[documentBasedNotes, timelineNoteIds, securitySolutionNotesEnabled]
|
||||
);
|
||||
|
||||
const noteIds = useMemo(() => {
|
||||
return securitySolutionNotesEnabled && !expandableFlyoutDisabled
|
||||
return securitySolutionNotesEnabled
|
||||
? documentBasedNotes.map((note) => note.noteId)
|
||||
: timelineNoteIds;
|
||||
}, [documentBasedNotes, timelineNoteIds, securitySolutionNotesEnabled, expandableFlyoutDisabled]);
|
||||
}, [documentBasedNotes, timelineNoteIds, securitySolutionNotesEnabled]);
|
||||
|
||||
return (
|
||||
<ActionsContainer>
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
/*
|
||||
* 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 { ExpandedDetailType } from '../../../../common/types';
|
||||
|
||||
export type FlyoutUrlState = ExpandedDetailType;
|
|
@ -1,103 +0,0 @@
|
|||
/*
|
||||
* 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 { useCallback, useEffect, useState } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import {
|
||||
dataTableSelectors,
|
||||
TableId,
|
||||
tableDefaults,
|
||||
dataTableActions,
|
||||
} from '@kbn/securitysolution-data-table';
|
||||
import { useIsExperimentalFeatureEnabled } from '../use_experimental_features';
|
||||
import { useInitializeUrlParam } from '../../utils/global_query_string';
|
||||
import { URL_PARAM_KEY } from '../use_url_state';
|
||||
import type { FlyoutUrlState } from './types';
|
||||
import { useShallowEqualSelector } from '../use_selector';
|
||||
import { getQueryStringKeyValue } from '../timeline/use_query_timeline_by_id_on_url_change';
|
||||
|
||||
/**
|
||||
* The state of the old flyout of the table in the Alerts page is stored in local storage.
|
||||
* This hook was created to initialize things and populate the url with the correct param and its value.
|
||||
* This is only be needed with the old flyout.
|
||||
* // TODO remove this hook entirely when we delete the old flyout code
|
||||
*/
|
||||
|
||||
export const useInitFlyoutFromUrlParam = () => {
|
||||
const { search } = useLocation();
|
||||
const expandableFlyoutDisabled = useIsExperimentalFeatureEnabled('expandableFlyoutDisabled');
|
||||
const [urlDetails, setUrlDetails] = useState<FlyoutUrlState | null>(null);
|
||||
const [hasLoadedUrlDetails, updateHasLoadedUrlDetails] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
const getDataTable = dataTableSelectors.getTableByIdSelector();
|
||||
|
||||
// Only allow the alerts page for now to be saved in the url state.
|
||||
// Allowing only one makes the transition to the expanded flyout much easier as well
|
||||
const dataTableCurrent = useShallowEqualSelector(
|
||||
(state) => getDataTable(state, TableId.alertsOnAlertsPage) ?? tableDefaults
|
||||
);
|
||||
|
||||
const onInitialize = useCallback(
|
||||
(initialState: FlyoutUrlState | null) => {
|
||||
if (expandableFlyoutDisabled && initialState != null && initialState.panelView) {
|
||||
setUrlDetails(initialState);
|
||||
}
|
||||
},
|
||||
[expandableFlyoutDisabled]
|
||||
);
|
||||
|
||||
const loadExpandedDetailFromUrl = useCallback(() => {
|
||||
if (!expandableFlyoutDisabled) return;
|
||||
|
||||
const { initialized, isLoading, totalCount, additionalFilters } = dataTableCurrent;
|
||||
const isTableLoaded = initialized && !isLoading && totalCount > 0;
|
||||
if (urlDetails) {
|
||||
if (!additionalFilters || !additionalFilters.showBuildingBlockAlerts) {
|
||||
// We want to show building block alerts when loading the flyout in case the alert is a building block alert
|
||||
dispatch(
|
||||
dataTableActions.updateShowBuildingBlockAlertsFilter({
|
||||
id: TableId.alertsOnAlertsPage,
|
||||
showBuildingBlockAlerts: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (isTableLoaded) {
|
||||
updateHasLoadedUrlDetails(true);
|
||||
dispatch(
|
||||
dataTableActions.toggleDetailPanel({
|
||||
id: TableId.alertsOnAlertsPage,
|
||||
...urlDetails,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}, [dataTableCurrent, dispatch, expandableFlyoutDisabled, urlDetails]);
|
||||
|
||||
// The alert page creates a default dataTable slice in redux initially that is later overriden when data is retrieved
|
||||
// We use the below to store the urlDetails on app load, and then set it when the table is done loading and has data
|
||||
useEffect(() => {
|
||||
if (expandableFlyoutDisabled && !hasLoadedUrlDetails) {
|
||||
loadExpandedDetailFromUrl();
|
||||
}
|
||||
}, [hasLoadedUrlDetails, expandableFlyoutDisabled, loadExpandedDetailFromUrl]);
|
||||
|
||||
// We check the url for the presence of the old `evenFlyout` parameter. If it exists replace it with the new `flyout` key.
|
||||
const eventFlyoutKey = getQueryStringKeyValue({ urlKey: URL_PARAM_KEY.eventFlyout, search });
|
||||
let currentKey = '';
|
||||
let newKey = '';
|
||||
if (expandableFlyoutDisabled) {
|
||||
if (eventFlyoutKey) {
|
||||
currentKey = URL_PARAM_KEY.eventFlyout;
|
||||
newKey = URL_PARAM_KEY.flyout;
|
||||
} else {
|
||||
currentKey = URL_PARAM_KEY.flyout;
|
||||
}
|
||||
}
|
||||
useInitializeUrlParam(currentKey, onInitialize, newKey);
|
||||
};
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* 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 { useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import {
|
||||
dataTableActions,
|
||||
dataTableSelectors,
|
||||
tableDefaults,
|
||||
TableId,
|
||||
} from '@kbn/securitysolution-data-table';
|
||||
import { useIsExperimentalFeatureEnabled } from '../use_experimental_features';
|
||||
import { ALERTS_PATH } from '../../../../common/constants';
|
||||
import { useUpdateUrlParam } from '../../utils/global_query_string';
|
||||
import { useShallowEqualSelector } from '../use_selector';
|
||||
import { URL_PARAM_KEY } from '../use_url_state';
|
||||
import type { FlyoutUrlState } from './types';
|
||||
|
||||
/**
|
||||
* The state of the old flyout of the table in the Alerts page is stored in local storage.
|
||||
* This hook was created to sync the data stored in local storage to the url.
|
||||
* This is only be needed with the old flyout.
|
||||
* // TODO remove this hook entirely when we delete the old flyout code
|
||||
*/
|
||||
export const useSyncFlyoutUrlParam = () => {
|
||||
const expandableFlyoutDisabled = useIsExperimentalFeatureEnabled('expandableFlyoutDisabled');
|
||||
const updateUrlParam = useUpdateUrlParam<FlyoutUrlState>(URL_PARAM_KEY.flyout);
|
||||
const { pathname } = useLocation();
|
||||
const dispatch = useDispatch();
|
||||
const getDataTable = dataTableSelectors.getTableByIdSelector();
|
||||
|
||||
// Only allow the alerts page for now to be saved in the url state
|
||||
const { expandedDetail } = useShallowEqualSelector(
|
||||
(state) => getDataTable(state, TableId.alertsOnAlertsPage) ?? tableDefaults
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!expandableFlyoutDisabled) return;
|
||||
|
||||
const isOnAlertsPage = pathname === ALERTS_PATH;
|
||||
if (isOnAlertsPage && expandedDetail != null && expandedDetail?.query) {
|
||||
updateUrlParam(expandedDetail.query.panelView ? expandedDetail.query : null);
|
||||
} else if (!isOnAlertsPage && expandedDetail?.query?.panelView) {
|
||||
// Close the detail panel as it's stored in a top level redux store maintained across views
|
||||
dispatch(
|
||||
dataTableActions.toggleDetailPanel({
|
||||
id: TableId.alertsOnAlertsPage,
|
||||
})
|
||||
);
|
||||
// Clear the reference from the url
|
||||
updateUrlParam(null);
|
||||
}
|
||||
}, [dispatch, expandedDetail, expandableFlyoutDisabled, pathname, updateUrlParam]);
|
||||
};
|
|
@ -12,8 +12,6 @@ import { useUpdateTimerangeOnPageChange } from './search_bar/use_update_timerang
|
|||
import { useInitTimelineFromUrlParam } from './timeline/use_init_timeline_url_param';
|
||||
import { useSyncTimelineUrlParam } from './timeline/use_sync_timeline_url_param';
|
||||
import { useQueryTimelineByIdOnUrlChange } from './timeline/use_query_timeline_by_id_on_url_change';
|
||||
import { useInitFlyoutFromUrlParam } from './flyout/use_init_flyout_url_param';
|
||||
import { useSyncFlyoutUrlParam } from './flyout/use_sync_flyout_url_param';
|
||||
|
||||
export const useUrlState = () => {
|
||||
useSyncGlobalQueryString();
|
||||
|
@ -23,8 +21,6 @@ export const useUrlState = () => {
|
|||
useInitTimelineFromUrlParam();
|
||||
useSyncTimelineUrlParam();
|
||||
useQueryTimelineByIdOnUrlChange();
|
||||
useInitFlyoutFromUrlParam();
|
||||
useSyncFlyoutUrlParam();
|
||||
};
|
||||
|
||||
export const URL_PARAM_KEY = {
|
||||
|
|
|
@ -29,11 +29,10 @@ export const getUseActionColumnHook =
|
|||
|
||||
// we only want to show the note icon if the expandable flyout and the new notes system are enabled
|
||||
// TODO delete most likely in 8.16
|
||||
const expandableFlyoutDisabled = useIsExperimentalFeatureEnabled('expandableFlyoutDisabled');
|
||||
const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled(
|
||||
'securitySolutionNotesEnabled'
|
||||
);
|
||||
if (expandableFlyoutDisabled || !securitySolutionNotesEnabled) {
|
||||
if (!securitySolutionNotesEnabled) {
|
||||
ACTION_BUTTON_COUNT--;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,9 +11,6 @@ import { AlertDetailsRedirect } from './alert_details_redirect';
|
|||
import { TestProviders } from '../../../common/mock';
|
||||
import { ALERTS_PATH, ALERT_DETAILS_REDIRECT_PATH } from '../../../../common/constants';
|
||||
import { mockHistory } from '../../../common/utils/route/mocks';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
|
||||
|
||||
jest.mock('../../../common/hooks/use_experimental_features');
|
||||
|
||||
jest.mock('../../../common/lib/kibana');
|
||||
|
||||
|
@ -149,37 +146,4 @@ describe('AlertDetailsRedirect', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('When expandable flyout is enabled', () => {
|
||||
beforeEach(() => {
|
||||
jest.mocked(useIsExperimentalFeatureEnabled).mockReturnValue(true);
|
||||
});
|
||||
|
||||
describe('when eventFlyout or flyout are not in the query', () => {
|
||||
it('redirects to the expected path with the correct query parameters', () => {
|
||||
const testSearch = `?index=${testIndex}×tamp=${testTimestamp}`;
|
||||
const historyMock = {
|
||||
...mockHistory,
|
||||
location: {
|
||||
hash: '',
|
||||
pathname: mockPathname,
|
||||
search: testSearch,
|
||||
state: '',
|
||||
},
|
||||
};
|
||||
render(
|
||||
<TestProviders>
|
||||
<Router history={historyMock}>
|
||||
<AlertDetailsRedirect />
|
||||
</Router>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
const [{ search, pathname }] = historyMock.replace.mock.lastCall;
|
||||
|
||||
expect(search as string).toMatch(/flyout.*/);
|
||||
expect(pathname).toEqual(ALERTS_PATH);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -13,7 +13,6 @@ import moment from 'moment';
|
|||
import { encode } from '@kbn/rison';
|
||||
import { ALERT_WORKFLOW_STATUS } from '@kbn/rule-data-utils';
|
||||
import type { FilterControlConfig } from '@kbn/alerts-ui-shared';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
|
||||
import { ALERTS_PATH, DEFAULT_ALERTS_INDEX } from '../../../../common/constants';
|
||||
import { URL_PARAM_KEY } from '../../../common/hooks/use_url_state';
|
||||
import { inputsSelectors } from '../../../common/store';
|
||||
|
@ -69,16 +68,11 @@ export const AlertDetailsRedirect = () => {
|
|||
|
||||
const currentFlyoutParams = searchParams.get(URL_PARAM_KEY.flyout);
|
||||
|
||||
const expandableFlyoutDisabled = useIsExperimentalFeatureEnabled('expandableFlyoutDisabled');
|
||||
|
||||
const urlParams = new URLSearchParams({
|
||||
[URL_PARAM_KEY.appQuery]: kqlAppQuery,
|
||||
[URL_PARAM_KEY.timerange]: timerange,
|
||||
[URL_PARAM_KEY.pageFilter]: pageFiltersQuery,
|
||||
[URL_PARAM_KEY.flyout]: resolveFlyoutParams(
|
||||
{ index, alertId, expandableFlyoutDisabled },
|
||||
currentFlyoutParams
|
||||
),
|
||||
[URL_PARAM_KEY.flyout]: resolveFlyoutParams({ index, alertId }, currentFlyoutParams),
|
||||
});
|
||||
|
||||
const url = `${ALERTS_PATH}?${urlParams.toString()}`;
|
||||
|
|
|
@ -11,7 +11,6 @@ import { expandableFlyoutStateFromEventMeta } from '../../../flyout/document_det
|
|||
export interface ResolveFlyoutParamsConfig {
|
||||
index: string;
|
||||
alertId: string;
|
||||
expandableFlyoutDisabled: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,27 +20,14 @@ export interface ResolveFlyoutParamsConfig {
|
|||
* with Share Button on the Expandable Flyout
|
||||
*/
|
||||
export const resolveFlyoutParams = (
|
||||
{ index, alertId, expandableFlyoutDisabled }: ResolveFlyoutParamsConfig,
|
||||
{ index, alertId }: ResolveFlyoutParamsConfig,
|
||||
currentParamsString: string | null
|
||||
) => {
|
||||
if (expandableFlyoutDisabled) {
|
||||
const legacyFlyoutString = encode({
|
||||
panelView: 'eventDetail',
|
||||
params: {
|
||||
eventId: alertId,
|
||||
indexName: index,
|
||||
},
|
||||
});
|
||||
return legacyFlyoutString;
|
||||
}
|
||||
|
||||
if (currentParamsString) {
|
||||
return currentParamsString;
|
||||
}
|
||||
|
||||
const modernFlyoutString = encode(
|
||||
return encode(
|
||||
expandableFlyoutStateFromEventMeta({ index, eventId: alertId, scopeId: 'alerts-page' })
|
||||
);
|
||||
|
||||
return modernFlyoutString;
|
||||
};
|
||||
|
|
|
@ -13,7 +13,6 @@ import { UsersQueries } from '../../../../../../common/search_strategy/security_
|
|||
import type { UserItem } from '../../../../../../common/search_strategy/security_solution/users/common';
|
||||
import { NOT_EVENT_KIND_ASSET_FILTER } from '../../../../../../common/search_strategy/security_solution/users/common';
|
||||
import { useSearchStrategy } from '../../../../../common/containers/use_search_strategy';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
|
||||
|
||||
export const OBSERVED_USER_QUERY_ID = 'observedUsersDetailsQuery';
|
||||
|
||||
|
@ -43,7 +42,6 @@ export const useObservedUserDetails = ({
|
|||
skip = false,
|
||||
startDate,
|
||||
}: UseUserDetails): [boolean, UserDetailsArgs] => {
|
||||
const expandableFlyoutDisabled = useIsExperimentalFeatureEnabled('expandableFlyoutDisabled');
|
||||
const {
|
||||
loading,
|
||||
result: response,
|
||||
|
@ -81,9 +79,9 @@ export const useObservedUserDetails = ({
|
|||
from: startDate,
|
||||
to: endDate,
|
||||
},
|
||||
filterQuery: !expandableFlyoutDisabled ? NOT_EVENT_KIND_ASSET_FILTER : undefined,
|
||||
filterQuery: NOT_EVENT_KIND_ASSET_FILTER,
|
||||
}),
|
||||
[endDate, indexNames, startDate, userName, expandableFlyoutDisabled]
|
||||
[endDate, indexNames, startDate, userName]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -20,7 +20,6 @@ import React, { useCallback, useMemo, useState } from 'react';
|
|||
import styled from 'styled-components';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
|
||||
import { useKibana } from '../../../../common/lib/kibana';
|
||||
import { DocumentDetailsRightPanelKey } from '../../../../flyout/document_details/shared/constants/panel_keys';
|
||||
import type { TimelineResultNote } from '../types';
|
||||
|
@ -29,7 +28,7 @@ import { MarkdownRenderer } from '../../../../common/components/markdown_editor'
|
|||
import { timelineActions, timelineSelectors } from '../../../store';
|
||||
import { NOTE_CONTENT_CLASS_NAME } from '../../timeline/body/helpers';
|
||||
import * as i18n from './translations';
|
||||
import { TimelineTabs, TimelineId } from '../../../../../common/types/timeline';
|
||||
import { TimelineId } from '../../../../../common/types/timeline';
|
||||
import { useDeepEqualSelector } from '../../../../common/hooks/use_selector';
|
||||
import { SourcererScopeName } from '../../../../sourcerer/store/model';
|
||||
import { useSourcererDataView } from '../../../../sourcerer/containers';
|
||||
|
@ -51,53 +50,27 @@ const ToggleEventDetailsButtonComponent: React.FC<ToggleEventDetailsButtonProps>
|
|||
eventId,
|
||||
timelineId,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const { selectedPatterns } = useSourcererDataView(SourcererScopeName.timeline);
|
||||
|
||||
const { telemetry } = useKibana().services;
|
||||
const { openFlyout } = useExpandableFlyoutApi();
|
||||
const expandableFlyoutDisabled = useIsExperimentalFeatureEnabled('expandableFlyoutDisabled');
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
const indexName = selectedPatterns.join(',');
|
||||
|
||||
if (!expandableFlyoutDisabled) {
|
||||
openFlyout({
|
||||
right: {
|
||||
id: DocumentDetailsRightPanelKey,
|
||||
params: {
|
||||
id: eventId,
|
||||
indexName,
|
||||
scopeId: timelineId,
|
||||
},
|
||||
openFlyout({
|
||||
right: {
|
||||
id: DocumentDetailsRightPanelKey,
|
||||
params: {
|
||||
id: eventId,
|
||||
indexName: selectedPatterns.join(','),
|
||||
scopeId: timelineId,
|
||||
},
|
||||
});
|
||||
telemetry.reportDetailsFlyoutOpened({
|
||||
location: timelineId,
|
||||
panel: 'right',
|
||||
});
|
||||
} else {
|
||||
dispatch(
|
||||
timelineActions.toggleDetailPanel({
|
||||
panelView: 'eventDetail',
|
||||
tabType: TimelineTabs.notes,
|
||||
id: timelineId,
|
||||
params: {
|
||||
eventId,
|
||||
indexName,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}, [
|
||||
dispatch,
|
||||
eventId,
|
||||
expandableFlyoutDisabled,
|
||||
openFlyout,
|
||||
selectedPatterns,
|
||||
telemetry,
|
||||
timelineId,
|
||||
]);
|
||||
},
|
||||
});
|
||||
telemetry.reportDetailsFlyoutOpened({
|
||||
location: timelineId,
|
||||
panel: 'right',
|
||||
});
|
||||
}, [eventId, openFlyout, selectedPatterns, telemetry, timelineId]);
|
||||
|
||||
return (
|
||||
<EuiButtonIcon
|
||||
|
|
|
@ -8,7 +8,6 @@ import { renderHook, act } from '@testing-library/react-hooks';
|
|||
import React from 'react';
|
||||
import type { UseDetailPanelConfig } from './use_detail_panel';
|
||||
import { useDetailPanel } from './use_detail_panel';
|
||||
import { timelineActions } from '../../../store';
|
||||
import { useDeepEqualSelector } from '../../../../common/hooks/use_selector';
|
||||
import { SourcererScopeName } from '../../../../sourcerer/store/model';
|
||||
import { TimelineId, TimelineTabs } from '../../../../../common/types/timeline';
|
||||
|
@ -33,14 +32,6 @@ jest.mock('../../../../common/lib/kibana', () => {
|
|||
jest.mock('../../../../common/hooks/use_selector');
|
||||
jest.mock('../../../store');
|
||||
|
||||
const mockDispatch = jest.fn();
|
||||
jest.mock('react-redux', () => {
|
||||
const original = jest.requireActual('react-redux');
|
||||
return {
|
||||
...original,
|
||||
useDispatch: () => mockDispatch,
|
||||
};
|
||||
});
|
||||
jest.mock('../../../../sourcerer/containers', () => {
|
||||
const mockSourcererReturn = {
|
||||
browserFields: {},
|
||||
|
@ -88,18 +79,6 @@ describe('useDetailPanel', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('should fire redux action to open event details panel', async () => {
|
||||
await act(async () => {
|
||||
const { result, waitForNextUpdate } = renderUseDetailPanel();
|
||||
await waitForNextUpdate();
|
||||
|
||||
result.current?.openEventDetailsPanel('123');
|
||||
|
||||
expect(mockDispatch).not.toHaveBeenCalled();
|
||||
expect(timelineActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
test('should show the details panel', async () => {
|
||||
mockGetExpandedDetail.mockImplementation(() => ({
|
||||
[TimelineTabs.session]: {
|
||||
|
|
|
@ -5,15 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useMemo, useCallback, useRef } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import React, { useMemo, useCallback } from 'react';
|
||||
import type { EntityType } from '@kbn/timelines-plugin/common';
|
||||
import { dataTableSelectors } from '@kbn/securitysolution-data-table';
|
||||
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
|
||||
import { useKibana } from '../../../../common/lib/kibana';
|
||||
import type { ExpandedDetailType } from '../../../../../common/types';
|
||||
import { getScopedActions, isInTableScope, isTimelineScope } from '../../../../helpers';
|
||||
import { isInTableScope, isTimelineScope } from '../../../../helpers';
|
||||
import { timelineSelectors } from '../../../store';
|
||||
import { useSourcererDataView } from '../../../../sourcerer/containers';
|
||||
import type { SourcererScopeName } from '../../../../sourcerer/store/model';
|
||||
|
@ -45,10 +42,8 @@ export const useDetailPanel = ({
|
|||
}: UseDetailPanelConfig): UseDetailPanelReturn => {
|
||||
const { telemetry } = useKibana().services;
|
||||
const { browserFields, selectedPatterns, runtimeMappings } = useSourcererDataView(sourcererScope);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { openFlyout } = useExpandableFlyoutApi();
|
||||
const expandableFlyoutDisabled = useIsExperimentalFeatureEnabled('expandableFlyoutDisabled');
|
||||
|
||||
const getScope = useMemo(() => {
|
||||
if (isTimelineScope(scopeId)) {
|
||||
|
@ -62,8 +57,6 @@ export const useDetailPanel = ({
|
|||
const expandedDetail = useDeepEqualSelector(
|
||||
(state) => ((getScope && getScope(state, scopeId)) ?? timelineDefaults)?.expandedDetail
|
||||
);
|
||||
const onPanelClose = useRef(() => {});
|
||||
const noopPanelClose = () => {};
|
||||
|
||||
const shouldShowDetailsPanel = useMemo(() => {
|
||||
if (
|
||||
|
@ -76,68 +69,34 @@ export const useDetailPanel = ({
|
|||
}
|
||||
return false;
|
||||
}, [expandedDetail, tabType]);
|
||||
const scopedActions = getScopedActions(scopeId);
|
||||
|
||||
// We could just surface load details panel, but rather than have users be concerned
|
||||
// of the config for a panel, they can just pass the base necessary values to a panel specific function
|
||||
const loadDetailsPanel = useCallback(
|
||||
(panelConfig?: ExpandedDetailType) => {
|
||||
if (panelConfig && scopedActions) {
|
||||
dispatch(
|
||||
scopedActions.toggleDetailPanel({
|
||||
...panelConfig,
|
||||
tabType,
|
||||
id: scopeId,
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
[scopedActions, scopeId, dispatch, tabType]
|
||||
);
|
||||
|
||||
const openEventDetailsPanel = useCallback(
|
||||
(eventId?: string, onClose?: () => void) => {
|
||||
if (!expandableFlyoutDisabled) {
|
||||
openFlyout({
|
||||
right: {
|
||||
id: DocumentDetailsRightPanelKey,
|
||||
params: {
|
||||
id: eventId,
|
||||
indexName: eventDetailsIndex,
|
||||
scopeId,
|
||||
},
|
||||
openFlyout({
|
||||
right: {
|
||||
id: DocumentDetailsRightPanelKey,
|
||||
params: {
|
||||
id: eventId,
|
||||
indexName: eventDetailsIndex,
|
||||
scopeId,
|
||||
},
|
||||
});
|
||||
telemetry.reportDetailsFlyoutOpened({
|
||||
location: scopeId,
|
||||
panel: 'right',
|
||||
});
|
||||
} else if (eventId) {
|
||||
loadDetailsPanel({
|
||||
panelView: 'eventDetail',
|
||||
params: { eventId, indexName: eventDetailsIndex },
|
||||
});
|
||||
onPanelClose.current = onClose ?? noopPanelClose;
|
||||
}
|
||||
},
|
||||
});
|
||||
telemetry.reportDetailsFlyoutOpened({
|
||||
location: scopeId,
|
||||
panel: 'right',
|
||||
});
|
||||
},
|
||||
[expandableFlyoutDisabled, openFlyout, eventDetailsIndex, scopeId, telemetry, loadDetailsPanel]
|
||||
[openFlyout, eventDetailsIndex, scopeId, telemetry]
|
||||
);
|
||||
|
||||
const handleOnDetailsPanelClosed = useCallback(() => {
|
||||
if (!expandableFlyoutDisabled) return;
|
||||
if (onPanelClose.current) onPanelClose.current();
|
||||
if (scopedActions) {
|
||||
dispatch(scopedActions.toggleDetailPanel({ tabType, id: scopeId }));
|
||||
}
|
||||
}, [expandableFlyoutDisabled, scopedActions, dispatch, tabType, scopeId]);
|
||||
|
||||
const DetailsPanel = useMemo(
|
||||
() =>
|
||||
shouldShowDetailsPanel ? (
|
||||
<DetailsPanelComponent
|
||||
browserFields={browserFields}
|
||||
entityType={entityType}
|
||||
handleOnPanelClosed={handleOnDetailsPanelClosed}
|
||||
handleOnPanelClosed={() => {}}
|
||||
isFlyoutView={isFlyoutView}
|
||||
runtimeMappings={runtimeMappings}
|
||||
tabType={tabType}
|
||||
|
@ -147,7 +106,6 @@ export const useDetailPanel = ({
|
|||
[
|
||||
browserFields,
|
||||
entityType,
|
||||
handleOnDetailsPanelClosed,
|
||||
isFlyoutView,
|
||||
runtimeMappings,
|
||||
shouldShowDetailsPanel,
|
||||
|
|
|
@ -11,7 +11,6 @@ import React, { useCallback, useMemo, useRef, useState } from 'react';
|
|||
import { useDispatch } from 'react-redux';
|
||||
import { isEventBuildingBlockType } from '@kbn/securitysolution-data-table';
|
||||
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
|
||||
import { DocumentDetailsRightPanelKey } from '../../../../../flyout/document_details/shared/constants/panel_keys';
|
||||
import { useDeepEqualSelector } from '../../../../../common/hooks/use_selector';
|
||||
import { useKibana } from '../../../../../common/lib/kibana';
|
||||
|
@ -43,7 +42,6 @@ import { useGetMappedNonEcsValue } from '../data_driven_columns';
|
|||
import { StatefulEventContext } from '../../../../../common/components/events_viewer/stateful_event_context';
|
||||
import type {
|
||||
ControlColumnProps,
|
||||
ExpandedDetailType,
|
||||
SetEventsDeleted,
|
||||
SetEventsLoading,
|
||||
} from '../../../../../../common/types';
|
||||
|
@ -112,7 +110,6 @@ const StatefulEventComponent: React.FC<Props> = ({
|
|||
const trGroupRef = useRef<HTMLDivElement | null>(null);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const expandableFlyoutDisabled = useIsExperimentalFeatureEnabled('expandableFlyoutDisabled');
|
||||
const { openFlyout } = useExpandableFlyoutApi();
|
||||
|
||||
// Store context in state rather than creating object in provider value={} to prevent re-renders caused by a new object being created
|
||||
|
@ -206,51 +203,21 @@ const StatefulEventComponent: React.FC<Props> = ({
|
|||
);
|
||||
|
||||
const handleOnEventDetailPanelOpened = useCallback(() => {
|
||||
const updatedExpandedDetail: ExpandedDetailType = {
|
||||
panelView: 'eventDetail',
|
||||
params: {
|
||||
eventId,
|
||||
indexName,
|
||||
refetch,
|
||||
},
|
||||
};
|
||||
|
||||
if (!expandableFlyoutDisabled) {
|
||||
openFlyout({
|
||||
right: {
|
||||
id: DocumentDetailsRightPanelKey,
|
||||
params: {
|
||||
id: eventId,
|
||||
indexName,
|
||||
scopeId: timelineId,
|
||||
},
|
||||
openFlyout({
|
||||
right: {
|
||||
id: DocumentDetailsRightPanelKey,
|
||||
params: {
|
||||
id: eventId,
|
||||
indexName,
|
||||
scopeId: timelineId,
|
||||
},
|
||||
});
|
||||
telemetry.reportDetailsFlyoutOpened({
|
||||
location: timelineId,
|
||||
panel: 'right',
|
||||
});
|
||||
} else {
|
||||
// opens the panel when clicking on the table row action
|
||||
dispatch(
|
||||
timelineActions.toggleDetailPanel({
|
||||
...updatedExpandedDetail,
|
||||
tabType,
|
||||
id: timelineId,
|
||||
})
|
||||
);
|
||||
}
|
||||
}, [
|
||||
eventId,
|
||||
indexName,
|
||||
refetch,
|
||||
expandableFlyoutDisabled,
|
||||
openFlyout,
|
||||
timelineId,
|
||||
telemetry,
|
||||
dispatch,
|
||||
tabType,
|
||||
]);
|
||||
},
|
||||
});
|
||||
telemetry.reportDetailsFlyoutOpened({
|
||||
location: timelineId,
|
||||
panel: 'right',
|
||||
});
|
||||
}, [eventId, indexName, openFlyout, timelineId, telemetry]);
|
||||
|
||||
const setEventsLoading = useCallback<SetEventsLoading>(
|
||||
({ eventIds, isLoading }) => {
|
||||
|
|
|
@ -11,17 +11,13 @@ import { waitFor } from '@testing-library/react';
|
|||
import { HostName } from './host_name';
|
||||
import { TestProviders } from '../../../../../common/mock';
|
||||
import { TimelineId, TimelineTabs } from '../../../../../../common/types/timeline';
|
||||
import { timelineActions } from '../../../../store';
|
||||
import { StatefulEventContext } from '../../../../../common/components/events_viewer/stateful_event_context';
|
||||
import { createTelemetryServiceMock } from '../../../../../common/lib/telemetry/telemetry_service.mock';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
|
||||
import { dataTableActions, TableId } from '@kbn/securitysolution-data-table';
|
||||
import { TableId } from '@kbn/securitysolution-data-table';
|
||||
|
||||
const mockedTelemetry = createTelemetryServiceMock();
|
||||
const mockOpenRightPanel = jest.fn();
|
||||
|
||||
jest.mock('../../../../../common/hooks/use_experimental_features');
|
||||
|
||||
jest.mock('@kbn/expandable-flyout', () => {
|
||||
return {
|
||||
useExpandableFlyoutApi: () => ({
|
||||
|
@ -31,14 +27,6 @@ jest.mock('@kbn/expandable-flyout', () => {
|
|||
};
|
||||
});
|
||||
|
||||
jest.mock('react-redux', () => {
|
||||
const origin = jest.requireActual('react-redux');
|
||||
return {
|
||||
...origin,
|
||||
useDispatch: jest.fn().mockReturnValue(jest.fn()),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../../../common/lib/kibana/kibana_react', () => {
|
||||
return {
|
||||
useKibana: () => ({
|
||||
|
@ -57,28 +45,6 @@ jest.mock('../../../../../common/components/draggables', () => ({
|
|||
DefaultDraggable: () => <div data-test-subj="DefaultDraggable" />,
|
||||
}));
|
||||
|
||||
jest.mock('../../../../store', () => {
|
||||
const original = jest.requireActual('../../../../store');
|
||||
return {
|
||||
...original,
|
||||
timelineActions: {
|
||||
...original.timelineActions,
|
||||
toggleDetailPanel: jest.fn(),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('@kbn/securitysolution-data-table', () => {
|
||||
const original = jest.requireActual('@kbn/securitysolution-data-table');
|
||||
return {
|
||||
...original,
|
||||
dataTableActions: {
|
||||
...original.dataTableActions,
|
||||
toggleDetailPanel: jest.fn(),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe('HostName', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
@ -129,8 +95,6 @@ describe('HostName', () => {
|
|||
|
||||
wrapper.find('[data-test-subj="host-details-button"]').last().simulate('click');
|
||||
await waitFor(() => {
|
||||
expect(dataTableActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
expect(timelineActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
expect(mockOpenRightPanel).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
@ -153,8 +117,6 @@ describe('HostName', () => {
|
|||
|
||||
wrapper.find('[data-test-subj="host-details-button"]').last().simulate('click');
|
||||
await waitFor(() => {
|
||||
expect(dataTableActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
expect(timelineActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
expect(mockOpenRightPanel).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
@ -177,82 +139,11 @@ describe('HostName', () => {
|
|||
|
||||
wrapper.find('[data-test-subj="host-details-button"]').last().simulate('click');
|
||||
await waitFor(() => {
|
||||
expect(dataTableActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
expect(timelineActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
expect(mockOpenRightPanel).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
test('should open old flyout on table', async () => {
|
||||
(useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation((feature: string) => {
|
||||
if (feature === 'expandableFlyoutDisabled') return true;
|
||||
});
|
||||
const context = {
|
||||
enableHostDetailsFlyout: true,
|
||||
enableIpDetailsFlyout: true,
|
||||
timelineID: TableId.alertsOnAlertsPage,
|
||||
tabType: TimelineTabs.query,
|
||||
};
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<StatefulEventContext.Provider value={context}>
|
||||
<HostName {...props} />
|
||||
</StatefulEventContext.Provider>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
wrapper.find('[data-test-subj="host-details-button"]').last().simulate('click');
|
||||
await waitFor(() => {
|
||||
expect(dataTableActions.toggleDetailPanel).toHaveBeenCalledWith({
|
||||
id: context.timelineID,
|
||||
panelView: 'hostDetail',
|
||||
params: {
|
||||
hostName: props.value,
|
||||
},
|
||||
tabType: context.tabType,
|
||||
});
|
||||
expect(timelineActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
expect(mockOpenRightPanel).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
test('should open old flyout in timeline', async () => {
|
||||
(useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation((feature: string) => {
|
||||
if (feature === 'expandableFlyoutDisabled') return true;
|
||||
});
|
||||
const context = {
|
||||
enableHostDetailsFlyout: true,
|
||||
enableIpDetailsFlyout: true,
|
||||
timelineID: TimelineId.active,
|
||||
tabType: TimelineTabs.query,
|
||||
};
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<StatefulEventContext.Provider value={context}>
|
||||
<HostName {...props} />
|
||||
</StatefulEventContext.Provider>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
wrapper.find('[data-test-subj="host-details-button"]').last().simulate('click');
|
||||
await waitFor(() => {
|
||||
expect(timelineActions.toggleDetailPanel).toHaveBeenCalledWith({
|
||||
id: context.timelineID,
|
||||
panelView: 'hostDetail',
|
||||
params: {
|
||||
hostName: props.value,
|
||||
},
|
||||
tabType: context.tabType,
|
||||
});
|
||||
expect(dataTableActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
expect(mockOpenRightPanel).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
test('should open expandable flyout on table', async () => {
|
||||
(useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation((feature: string) => {
|
||||
if (feature === 'expandableFlyoutDisabled') return false;
|
||||
});
|
||||
const context = {
|
||||
enableHostDetailsFlyout: true,
|
||||
enableIpDetailsFlyout: true,
|
||||
|
@ -278,15 +169,10 @@ describe('HostName', () => {
|
|||
isDraggable: false,
|
||||
},
|
||||
});
|
||||
expect(timelineActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
expect(dataTableActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
test('should open expandable flyout in timeline', async () => {
|
||||
(useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation((feature: string) => {
|
||||
if (feature === 'expandableFlyoutDisabled') return false;
|
||||
});
|
||||
const context = {
|
||||
enableHostDetailsFlyout: true,
|
||||
enableIpDetailsFlyout: true,
|
||||
|
@ -312,8 +198,6 @@ describe('HostName', () => {
|
|||
isDraggable: false,
|
||||
},
|
||||
});
|
||||
expect(timelineActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
expect(dataTableActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,16 +7,11 @@
|
|||
|
||||
import React, { useCallback, useContext, useMemo } from 'react';
|
||||
import type { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { isString } from 'lodash/fp';
|
||||
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
|
||||
import { HostPanelKey } from '../../../../../flyout/entity_details/host_right';
|
||||
import type { ExpandedDetailType } from '../../../../../../common/types';
|
||||
import { StatefulEventContext } from '../../../../../common/components/events_viewer/stateful_event_context';
|
||||
import { getScopedActions } from '../../../../../helpers';
|
||||
import { HostDetailsLink } from '../../../../../common/components/links';
|
||||
import type { TimelineTabs } from '../../../../../../common/types/timeline';
|
||||
import { DefaultDraggable } from '../../../../../common/components/draggables';
|
||||
import { getEmptyTagValue } from '../../../../../common/components/empty_value';
|
||||
import { TruncatableText } from '../../../../../common/components/truncatable_text';
|
||||
|
@ -48,10 +43,8 @@ const HostNameComponent: React.FC<Props> = ({
|
|||
title,
|
||||
value,
|
||||
}) => {
|
||||
const expandableFlyoutDisabled = useIsExperimentalFeatureEnabled('expandableFlyoutDisabled');
|
||||
const { openRightPanel } = useExpandableFlyoutApi();
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const eventContext = useContext(StatefulEventContext);
|
||||
const hostName = `${value}`;
|
||||
const isInTimelineContext =
|
||||
|
@ -69,48 +62,19 @@ const HostNameComponent: React.FC<Props> = ({
|
|||
return;
|
||||
}
|
||||
|
||||
const { timelineID, tabType } = eventContext;
|
||||
const { timelineID } = eventContext;
|
||||
|
||||
if (!expandableFlyoutDisabled) {
|
||||
openRightPanel({
|
||||
id: HostPanelKey,
|
||||
params: {
|
||||
hostName,
|
||||
contextID: contextId,
|
||||
scopeId: timelineID,
|
||||
isDraggable,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const updatedExpandedDetail: ExpandedDetailType = {
|
||||
panelView: 'hostDetail',
|
||||
params: {
|
||||
hostName,
|
||||
},
|
||||
};
|
||||
const scopedActions = getScopedActions(timelineID);
|
||||
if (scopedActions) {
|
||||
dispatch(
|
||||
scopedActions.toggleDetailPanel({
|
||||
...updatedExpandedDetail,
|
||||
id: timelineID,
|
||||
tabType: tabType as TimelineTabs,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
openRightPanel({
|
||||
id: HostPanelKey,
|
||||
params: {
|
||||
hostName,
|
||||
contextID: contextId,
|
||||
scopeId: timelineID,
|
||||
isDraggable,
|
||||
},
|
||||
});
|
||||
},
|
||||
[
|
||||
contextId,
|
||||
dispatch,
|
||||
eventContext,
|
||||
expandableFlyoutDisabled,
|
||||
hostName,
|
||||
isDraggable,
|
||||
isInTimelineContext,
|
||||
onClick,
|
||||
openRightPanel,
|
||||
]
|
||||
[contextId, eventContext, hostName, isDraggable, isInTimelineContext, onClick, openRightPanel]
|
||||
);
|
||||
|
||||
// The below is explicitly defined this way as the onClick takes precedence when it and the href are both defined
|
||||
|
|
|
@ -10,18 +10,14 @@ import { waitFor } from '@testing-library/react';
|
|||
|
||||
import { TestProviders } from '../../../../../common/mock';
|
||||
import { TimelineId, TimelineTabs } from '../../../../../../common/types/timeline';
|
||||
import { timelineActions } from '../../../../store';
|
||||
import { UserName } from './user_name';
|
||||
import { StatefulEventContext } from '../../../../../common/components/events_viewer/stateful_event_context';
|
||||
import { createTelemetryServiceMock } from '../../../../../common/lib/telemetry/telemetry_service.mock';
|
||||
import { dataTableActions, TableId } from '@kbn/securitysolution-data-table';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
|
||||
import { TableId } from '@kbn/securitysolution-data-table';
|
||||
|
||||
const mockedTelemetry = createTelemetryServiceMock();
|
||||
const mockOpenRightPanel = jest.fn();
|
||||
|
||||
jest.mock('../../../../../common/hooks/use_experimental_features');
|
||||
|
||||
jest.mock('@kbn/expandable-flyout', () => {
|
||||
return {
|
||||
useExpandableFlyoutApi: () => ({
|
||||
|
@ -31,14 +27,6 @@ jest.mock('@kbn/expandable-flyout', () => {
|
|||
};
|
||||
});
|
||||
|
||||
jest.mock('react-redux', () => {
|
||||
const origin = jest.requireActual('react-redux');
|
||||
return {
|
||||
...origin,
|
||||
useDispatch: jest.fn().mockReturnValue(jest.fn()),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../../../common/lib/kibana/kibana_react', () => {
|
||||
return {
|
||||
useKibana: () => ({
|
||||
|
@ -57,28 +45,6 @@ jest.mock('../../../../../common/components/draggables', () => ({
|
|||
DefaultDraggable: () => <div data-test-subj="DefaultDraggable" />,
|
||||
}));
|
||||
|
||||
jest.mock('../../../../store', () => {
|
||||
const original = jest.requireActual('../../../../store');
|
||||
return {
|
||||
...original,
|
||||
timelineActions: {
|
||||
...original.timelineActions,
|
||||
toggleDetailPanel: jest.fn(),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('@kbn/securitysolution-data-table', () => {
|
||||
const original = jest.requireActual('@kbn/securitysolution-data-table');
|
||||
return {
|
||||
...original,
|
||||
dataTableActions: {
|
||||
...original.dataTableActions,
|
||||
toggleDetailPanel: jest.fn(),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe('UserName', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
@ -127,8 +93,6 @@ describe('UserName', () => {
|
|||
|
||||
wrapper.find('[data-test-subj="users-link-anchor"]').last().simulate('click');
|
||||
await waitFor(() => {
|
||||
expect(dataTableActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
expect(timelineActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
expect(mockOpenRightPanel).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
@ -151,82 +115,11 @@ describe('UserName', () => {
|
|||
|
||||
wrapper.find('[data-test-subj="users-link-anchor"]').last().simulate('click');
|
||||
await waitFor(() => {
|
||||
expect(dataTableActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
expect(timelineActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
expect(mockOpenRightPanel).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
test('should open old flyout on table', async () => {
|
||||
(useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation((feature: string) => {
|
||||
if (feature === 'expandableFlyoutDisabled') return true;
|
||||
});
|
||||
const context = {
|
||||
enableHostDetailsFlyout: true,
|
||||
enableIpDetailsFlyout: true,
|
||||
timelineID: TableId.alertsOnAlertsPage,
|
||||
tabType: TimelineTabs.query,
|
||||
};
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<StatefulEventContext.Provider value={context}>
|
||||
<UserName {...props} />
|
||||
</StatefulEventContext.Provider>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
wrapper.find('[data-test-subj="users-link-anchor"]').last().simulate('click');
|
||||
await waitFor(() => {
|
||||
expect(dataTableActions.toggleDetailPanel).toHaveBeenCalledWith({
|
||||
id: context.timelineID,
|
||||
panelView: 'userDetail',
|
||||
params: {
|
||||
userName: props.value,
|
||||
},
|
||||
tabType: context.tabType,
|
||||
});
|
||||
expect(timelineActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
expect(mockOpenRightPanel).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
test('should open old flyout in timeline', async () => {
|
||||
(useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation((feature: string) => {
|
||||
if (feature === 'expandableFlyoutDisabled') return true;
|
||||
});
|
||||
const context = {
|
||||
enableHostDetailsFlyout: true,
|
||||
enableIpDetailsFlyout: true,
|
||||
timelineID: TimelineId.active,
|
||||
tabType: TimelineTabs.query,
|
||||
};
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<StatefulEventContext.Provider value={context}>
|
||||
<UserName {...props} />
|
||||
</StatefulEventContext.Provider>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
wrapper.find('[data-test-subj="users-link-anchor"]').last().simulate('click');
|
||||
await waitFor(() => {
|
||||
expect(timelineActions.toggleDetailPanel).toHaveBeenCalledWith({
|
||||
id: context.timelineID,
|
||||
panelView: 'userDetail',
|
||||
params: {
|
||||
userName: props.value,
|
||||
},
|
||||
tabType: context.tabType,
|
||||
});
|
||||
expect(dataTableActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
expect(mockOpenRightPanel).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
test('should open expandable flyout on table', async () => {
|
||||
(useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation((feature: string) => {
|
||||
if (feature === 'expandableFlyoutDisabled') return false;
|
||||
});
|
||||
const context = {
|
||||
enableHostDetailsFlyout: true,
|
||||
enableIpDetailsFlyout: true,
|
||||
|
@ -252,15 +145,10 @@ describe('UserName', () => {
|
|||
isDraggable: false,
|
||||
},
|
||||
});
|
||||
expect(timelineActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
expect(dataTableActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
test('should open expandable flyout in timeline', async () => {
|
||||
(useIsExperimentalFeatureEnabled as jest.Mock).mockImplementation((feature: string) => {
|
||||
if (feature === 'expandableFlyoutDisabled') return false;
|
||||
});
|
||||
const context = {
|
||||
enableHostDetailsFlyout: true,
|
||||
enableIpDetailsFlyout: true,
|
||||
|
@ -286,8 +174,6 @@ describe('UserName', () => {
|
|||
isDraggable: false,
|
||||
},
|
||||
});
|
||||
expect(timelineActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
expect(dataTableActions.toggleDetailPanel).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,15 +7,10 @@
|
|||
|
||||
import React, { useCallback, useContext, useMemo } from 'react';
|
||||
import type { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { isString } from 'lodash/fp';
|
||||
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
|
||||
import { UserPanelKey } from '../../../../../flyout/entity_details/user_right';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
|
||||
import { StatefulEventContext } from '../../../../../common/components/events_viewer/stateful_event_context';
|
||||
import type { ExpandedDetailType } from '../../../../../../common/types';
|
||||
import { getScopedActions } from '../../../../../helpers';
|
||||
import type { TimelineTabs } from '../../../../../../common/types/timeline';
|
||||
import { DefaultDraggable } from '../../../../../common/components/draggables';
|
||||
import { getEmptyTagValue } from '../../../../../common/components/empty_value';
|
||||
import { UserDetailsLink } from '../../../../../common/components/links';
|
||||
|
@ -48,9 +43,7 @@ const UserNameComponent: React.FC<Props> = ({
|
|||
title,
|
||||
value,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const eventContext = useContext(StatefulEventContext);
|
||||
const expandableFlyoutDisabled = useIsExperimentalFeatureEnabled('expandableFlyoutDisabled');
|
||||
const userName = `${value}`;
|
||||
const isInTimelineContext = userName && eventContext?.timelineID;
|
||||
const { openRightPanel } = useExpandableFlyoutApi();
|
||||
|
@ -67,48 +60,19 @@ const UserNameComponent: React.FC<Props> = ({
|
|||
return;
|
||||
}
|
||||
|
||||
const { timelineID, tabType } = eventContext;
|
||||
const { timelineID } = eventContext;
|
||||
|
||||
if (!expandableFlyoutDisabled) {
|
||||
openRightPanel({
|
||||
id: UserPanelKey,
|
||||
params: {
|
||||
userName,
|
||||
contextID: contextId,
|
||||
scopeId: timelineID,
|
||||
isDraggable,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const updatedExpandedDetail: ExpandedDetailType = {
|
||||
panelView: 'userDetail',
|
||||
params: {
|
||||
userName,
|
||||
},
|
||||
};
|
||||
const scopedActions = getScopedActions(timelineID);
|
||||
if (scopedActions) {
|
||||
dispatch(
|
||||
scopedActions.toggleDetailPanel({
|
||||
...updatedExpandedDetail,
|
||||
id: timelineID,
|
||||
tabType: tabType as TimelineTabs,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
openRightPanel({
|
||||
id: UserPanelKey,
|
||||
params: {
|
||||
userName,
|
||||
contextID: contextId,
|
||||
scopeId: timelineID,
|
||||
isDraggable,
|
||||
},
|
||||
});
|
||||
},
|
||||
[
|
||||
contextId,
|
||||
dispatch,
|
||||
eventContext,
|
||||
expandableFlyoutDisabled,
|
||||
isDraggable,
|
||||
isInTimelineContext,
|
||||
onClick,
|
||||
openRightPanel,
|
||||
userName,
|
||||
]
|
||||
[contextId, eventContext, isDraggable, isInTimelineContext, onClick, openRightPanel, userName]
|
||||
);
|
||||
|
||||
// The below is explicitly defined this way as the onClick takes precedence when it and the href are both defined
|
||||
|
|
|
@ -145,7 +145,6 @@ export const EqlTabContentComponent: React.FC<Props> = ({
|
|||
timerangeKind,
|
||||
});
|
||||
|
||||
const expandableFlyoutDisabled = useIsExperimentalFeatureEnabled('expandableFlyoutDisabled');
|
||||
const { openFlyout } = useExpandableFlyoutApi();
|
||||
const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled(
|
||||
'securitySolutionNotesEnabled'
|
||||
|
@ -168,7 +167,7 @@ export const EqlTabContentComponent: React.FC<Props> = ({
|
|||
const onToggleShowNotes = useCallback(
|
||||
(eventId?: string) => {
|
||||
const indexName = selectedPatterns.join(',');
|
||||
if (eventId && !expandableFlyoutDisabled && securitySolutionNotesEnabled) {
|
||||
if (eventId && securitySolutionNotesEnabled) {
|
||||
openFlyout({
|
||||
right: {
|
||||
id: DocumentDetailsRightPanelKey,
|
||||
|
@ -205,7 +204,6 @@ export const EqlTabContentComponent: React.FC<Props> = ({
|
|||
}
|
||||
},
|
||||
[
|
||||
expandableFlyoutDisabled,
|
||||
openFlyout,
|
||||
securitySolutionNotesEnabled,
|
||||
selectedPatterns,
|
||||
|
|
|
@ -181,7 +181,6 @@ export const PinnedTabContentComponent: React.FC<Props> = ({
|
|||
timerangeKind: undefined,
|
||||
});
|
||||
|
||||
const expandableFlyoutDisabled = useIsExperimentalFeatureEnabled('expandableFlyoutDisabled');
|
||||
const { openFlyout } = useExpandableFlyoutApi();
|
||||
const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled(
|
||||
'securitySolutionNotesEnabled'
|
||||
|
@ -204,7 +203,7 @@ export const PinnedTabContentComponent: React.FC<Props> = ({
|
|||
const onToggleShowNotes = useCallback(
|
||||
(eventId?: string) => {
|
||||
const indexName = selectedPatterns.join(',');
|
||||
if (eventId && !expandableFlyoutDisabled && securitySolutionNotesEnabled) {
|
||||
if (eventId && securitySolutionNotesEnabled) {
|
||||
openFlyout({
|
||||
right: {
|
||||
id: DocumentDetailsRightPanelKey,
|
||||
|
@ -241,7 +240,6 @@ export const PinnedTabContentComponent: React.FC<Props> = ({
|
|||
}
|
||||
},
|
||||
[
|
||||
expandableFlyoutDisabled,
|
||||
openFlyout,
|
||||
securitySolutionNotesEnabled,
|
||||
selectedPatterns,
|
||||
|
|
|
@ -211,7 +211,6 @@ export const QueryTabContentComponent: React.FC<Props> = ({
|
|||
timerangeKind,
|
||||
});
|
||||
|
||||
const expandableFlyoutDisabled = useIsExperimentalFeatureEnabled('expandableFlyoutDisabled');
|
||||
const { openFlyout } = useExpandableFlyoutApi();
|
||||
const securitySolutionNotesEnabled = useIsExperimentalFeatureEnabled(
|
||||
'securitySolutionNotesEnabled'
|
||||
|
@ -234,7 +233,7 @@ export const QueryTabContentComponent: React.FC<Props> = ({
|
|||
const onToggleShowNotes = useCallback(
|
||||
(eventId?: string) => {
|
||||
const indexName = selectedPatterns.join(',');
|
||||
if (eventId && !expandableFlyoutDisabled && securitySolutionNotesEnabled) {
|
||||
if (eventId && securitySolutionNotesEnabled) {
|
||||
openFlyout({
|
||||
right: {
|
||||
id: DocumentDetailsRightPanelKey,
|
||||
|
@ -271,7 +270,6 @@ export const QueryTabContentComponent: React.FC<Props> = ({
|
|||
}
|
||||
},
|
||||
[
|
||||
expandableFlyoutDisabled,
|
||||
openFlyout,
|
||||
securitySolutionNotesEnabled,
|
||||
selectedPatterns,
|
||||
|
|
|
@ -776,332 +776,148 @@ 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.'
|
||||
);
|
||||
});
|
||||
},
|
||||
SPECIAL_TEST_TIMEOUT
|
||||
);
|
||||
it(
|
||||
'should be able to add notes using EuiFlyout',
|
||||
async () => {
|
||||
renderTestComponents();
|
||||
expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
|
||||
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();
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
|
||||
fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('add-note-container')).toBeVisible();
|
||||
});
|
||||
},
|
||||
SPECIAL_TEST_TIMEOUT
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('add-note-container')).toBeVisible();
|
||||
});
|
||||
|
||||
it(
|
||||
'should be cancel adding notes',
|
||||
async () => {
|
||||
renderTestComponents();
|
||||
expect(await screen.findByTestId('discoverDocTable')).toBeVisible();
|
||||
userEvent.type(screen.getByTestId('euiMarkdownEditorTextArea'), 'Test Note 1');
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('timeline-notes-button-small')).not.toBeDisabled();
|
||||
});
|
||||
expect(screen.getByTestId('cancel')).not.toBeDisabled();
|
||||
|
||||
fireEvent.click(screen.getByTestId('timeline-notes-button-small'));
|
||||
fireEvent.click(screen.getByTestId('cancel'));
|
||||
|
||||
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('add-note-container')).not.toBeInTheDocument();
|
||||
});
|
||||
},
|
||||
SPECIAL_TEST_TIMEOUT
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@ const onChangePageMock = jest.fn();
|
|||
|
||||
const openFlyoutMock = jest.fn();
|
||||
const closeFlyoutMock = jest.fn();
|
||||
const isExpandableFlyoutDisabled = false;
|
||||
|
||||
jest.mock('../hooks/use_unified_timeline_expandable_flyout');
|
||||
|
||||
|
@ -99,7 +98,6 @@ describe('unified data table', () => {
|
|||
beforeEach(() => {
|
||||
(useSourcererDataView as jest.Mock).mockReturnValue(mockSourcererScope);
|
||||
(useUnifiedTableExpandableFlyout as jest.Mock).mockReturnValue({
|
||||
isExpandableFlyoutDisabled,
|
||||
openFlyout: openFlyoutMock,
|
||||
closeFlyout: closeFlyoutMock,
|
||||
});
|
||||
|
|
|
@ -19,7 +19,7 @@ import { RowRendererCount } from '../../../../../../common/api/timeline';
|
|||
import { EmptyComponent } from '../../../../../common/lib/cell_actions/helpers';
|
||||
import { withDataView } from '../../../../../common/components/with_data_view';
|
||||
import { StatefulEventContext } from '../../../../../common/components/events_viewer/stateful_event_context';
|
||||
import type { ExpandedDetailTimeline, ExpandedDetailType } from '../../../../../../common/types';
|
||||
import type { ExpandedDetailTimeline } from '../../../../../../common/types';
|
||||
import type { TimelineItem } from '../../../../../../common/search_strategy';
|
||||
import { useKibana } from '../../../../../common/lib/kibana';
|
||||
import type {
|
||||
|
@ -29,12 +29,7 @@ import type {
|
|||
ToggleDetailPanel,
|
||||
TimelineTabs,
|
||||
} from '../../../../../../common/types/timeline';
|
||||
import { TimelineId } from '../../../../../../common/types/timeline';
|
||||
import type { State, inputsModel } from '../../../../../common/store';
|
||||
import { SourcererScopeName } from '../../../../../sourcerer/store/model';
|
||||
import { useSourcererDataView } from '../../../../../sourcerer/containers';
|
||||
import { activeTimeline } from '../../../../containers/active_timeline_context';
|
||||
import { DetailsPanel } from '../../../side_panel';
|
||||
import { SecurityCellActionsTrigger } from '../../../../../app/actions/constants';
|
||||
import { getFormattedFields } from '../../body/renderers/formatted_field_udt';
|
||||
import ToolbarAdditionalControls from './toolbar_additional_controls';
|
||||
|
@ -147,13 +142,9 @@ export const TimelineDataTableComponent: React.FC<DataTableProps> = memo(
|
|||
setExpandedDoc((prev) => (!prev ? prev : undefined));
|
||||
}, []);
|
||||
|
||||
const { openFlyout, closeFlyout, isExpandableFlyoutDisabled } = useUnifiedTableExpandableFlyout(
|
||||
{
|
||||
onClose: onCloseExpandableFlyout,
|
||||
}
|
||||
);
|
||||
|
||||
const { browserFields, runtimeMappings } = useSourcererDataView(SourcererScopeName.timeline);
|
||||
const { openFlyout, closeFlyout } = useUnifiedTableExpandableFlyout({
|
||||
onClose: onCloseExpandableFlyout,
|
||||
});
|
||||
|
||||
const showTimeCol = useMemo(() => !!dataView && !!dataView.timeFieldName, [dataView]);
|
||||
|
||||
|
@ -168,57 +159,24 @@ export const TimelineDataTableComponent: React.FC<DataTableProps> = memo(
|
|||
|
||||
const handleOnEventDetailPanelOpened = useCallback(
|
||||
(eventData: DataTableRecord & TimelineItem) => {
|
||||
const updatedExpandedDetail: ExpandedDetailType = {
|
||||
panelView: 'eventDetail',
|
||||
params: {
|
||||
eventId: eventData._id,
|
||||
indexName: eventData.ecs._index ?? '', // TODO: fix type error
|
||||
refetch,
|
||||
},
|
||||
};
|
||||
|
||||
if (!isExpandableFlyoutDisabled) {
|
||||
openFlyout({
|
||||
right: {
|
||||
id: DocumentDetailsRightPanelKey,
|
||||
params: {
|
||||
id: eventData._id,
|
||||
indexName: eventData.ecs._index ?? '',
|
||||
scopeId: timelineId,
|
||||
},
|
||||
openFlyout({
|
||||
right: {
|
||||
id: DocumentDetailsRightPanelKey,
|
||||
params: {
|
||||
id: eventData._id,
|
||||
indexName: eventData.ecs._index ?? '',
|
||||
scopeId: timelineId,
|
||||
},
|
||||
});
|
||||
telemetry.reportDetailsFlyoutOpened({
|
||||
location: timelineId,
|
||||
panel: 'right',
|
||||
});
|
||||
} else {
|
||||
dispatch(
|
||||
timelineActions.toggleDetailPanel({
|
||||
...updatedExpandedDetail,
|
||||
tabType: activeTab,
|
||||
id: timelineId,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
activeTimeline.toggleExpandedDetail({ ...updatedExpandedDetail });
|
||||
},
|
||||
});
|
||||
telemetry.reportDetailsFlyoutOpened({
|
||||
location: timelineId,
|
||||
panel: 'right',
|
||||
});
|
||||
},
|
||||
[refetch, isExpandableFlyoutDisabled, openFlyout, timelineId, telemetry, dispatch, activeTab]
|
||||
[openFlyout, timelineId, telemetry]
|
||||
);
|
||||
|
||||
const onTimelineLegacyFlyoutClose = useCallback(() => {
|
||||
if (
|
||||
expandedDetail[activeTab]?.panelView &&
|
||||
timelineId === TimelineId.active &&
|
||||
showExpandedDetails
|
||||
) {
|
||||
activeTimeline.toggleExpandedDetail({});
|
||||
}
|
||||
setExpandedDoc(undefined);
|
||||
onEventClosed({ tabType: activeTab, id: timelineId });
|
||||
}, [expandedDetail, activeTab, timelineId, showExpandedDetails, onEventClosed]);
|
||||
|
||||
const onSetExpandedDoc = useCallback(
|
||||
(newDoc?: DataTableRecord) => {
|
||||
if (newDoc) {
|
||||
|
@ -228,20 +186,10 @@ export const TimelineDataTableComponent: React.FC<DataTableProps> = memo(
|
|||
handleOnEventDetailPanelOpened(timelineDoc);
|
||||
}
|
||||
} else {
|
||||
if (!isExpandableFlyoutDisabled) {
|
||||
closeFlyout();
|
||||
return;
|
||||
}
|
||||
onTimelineLegacyFlyoutClose();
|
||||
closeFlyout();
|
||||
}
|
||||
},
|
||||
[
|
||||
tableRows,
|
||||
handleOnEventDetailPanelOpened,
|
||||
onTimelineLegacyFlyoutClose,
|
||||
closeFlyout,
|
||||
isExpandableFlyoutDisabled,
|
||||
]
|
||||
[tableRows, handleOnEventDetailPanelOpened, closeFlyout]
|
||||
);
|
||||
|
||||
const onColumnResize = useCallback(
|
||||
|
@ -468,16 +416,6 @@ export const TimelineDataTableComponent: React.FC<DataTableProps> = memo(
|
|||
externalControlColumns={leadingControlColumns}
|
||||
cellContext={cellContext}
|
||||
/>
|
||||
{showExpandedDetails && isExpandableFlyoutDisabled && (
|
||||
<DetailsPanel
|
||||
browserFields={browserFields}
|
||||
handleOnPanelClosed={onTimelineLegacyFlyoutClose}
|
||||
runtimeMappings={runtimeMappings}
|
||||
tabType={activeTab}
|
||||
scopeId={timelineId}
|
||||
isFlyoutView
|
||||
/>
|
||||
)}
|
||||
</StyledTimelineUnifiedDataTable>
|
||||
</StatefulEventContext.Provider>
|
||||
);
|
||||
|
|
|
@ -5,14 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { useUnifiedTableExpandableFlyout } from './use_unified_timeline_expandable_flyout';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { URL_PARAM_KEY } from '../../../../../common/hooks/use_url_state';
|
||||
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
|
||||
|
||||
jest.mock('../../../../../common/hooks/use_experimental_features');
|
||||
jest.mock('@kbn/kibana-react-plugin/public');
|
||||
jest.mock('react-router-dom', () => {
|
||||
return {
|
||||
|
@ -24,62 +22,22 @@ jest.mock('@kbn/expandable-flyout');
|
|||
const onFlyoutCloseMock = jest.fn();
|
||||
|
||||
describe('useUnifiedTimelineExpandableFlyout', () => {
|
||||
it('should disable expandable flyout when expandableFlyoutDisabled flag is true', () => {
|
||||
(useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
search: `?${URL_PARAM_KEY.timelineFlyout}=(test:value)`,
|
||||
});
|
||||
(useExpandableFlyoutApi as jest.Mock).mockReturnValue({
|
||||
openFlyout: jest.fn(),
|
||||
closeFlyout: jest.fn(),
|
||||
});
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useUnifiedTableExpandableFlyout({
|
||||
onClose: onFlyoutCloseMock,
|
||||
})
|
||||
);
|
||||
|
||||
expect(result.current.isExpandableFlyoutDisabled).toBe(true);
|
||||
});
|
||||
|
||||
describe('when expandable flyout is enabled', () => {
|
||||
beforeEach(() => {
|
||||
(useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false);
|
||||
(useExpandableFlyoutApi as jest.Mock).mockReturnValue({
|
||||
openFlyout: jest.fn(),
|
||||
closeFlyout: jest.fn(),
|
||||
});
|
||||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
search: `${URL_PARAM_KEY.timelineFlyout}=(test:value)`,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should mark flyout as closed when location is empty', () => {
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
search: '',
|
||||
});
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useUnifiedTableExpandableFlyout({
|
||||
onClose: onFlyoutCloseMock,
|
||||
})
|
||||
);
|
||||
|
||||
expect(result.current.isExpandableFlyoutDisabled).toBe(false);
|
||||
});
|
||||
|
||||
it('should mark flyout as open when location has `timelineFlyout`', () => {
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
search: `${URL_PARAM_KEY.timelineFlyout}=(test:value)`,
|
||||
});
|
||||
|
||||
const { result } = renderHook(() =>
|
||||
useUnifiedTableExpandableFlyout({
|
||||
onClose: onFlyoutCloseMock,
|
||||
})
|
||||
);
|
||||
|
||||
expect(result.current.isExpandableFlyoutDisabled).toBe(false);
|
||||
});
|
||||
|
||||
it('should mark flyout as close when location has empty `timelineFlyout`', () => {
|
||||
const { result, rerender } = renderHook(() =>
|
||||
useUnifiedTableExpandableFlyout({
|
||||
|
@ -87,8 +45,6 @@ describe('useUnifiedTimelineExpandableFlyout', () => {
|
|||
})
|
||||
);
|
||||
|
||||
expect(result.current.isExpandableFlyoutDisabled).toBe(false);
|
||||
|
||||
(useLocation as jest.Mock).mockReturnValue({
|
||||
search: `${URL_PARAM_KEY.timelineFlyout}=()`,
|
||||
});
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
|
||||
import { URL_PARAM_KEY } from '../../../../../common/hooks/use_url_state';
|
||||
|
||||
const EMPTY_TIMELINE_FLYOUT_SEARCH_PARAMS = '()';
|
||||
|
@ -20,8 +19,6 @@ interface UseUnifiedTableExpandableFlyoutArgs {
|
|||
export const useUnifiedTableExpandableFlyout = ({
|
||||
onClose,
|
||||
}: UseUnifiedTableExpandableFlyoutArgs) => {
|
||||
const expandableFlyoutDisabled = useIsExperimentalFeatureEnabled('expandableFlyoutDisabled');
|
||||
|
||||
const location = useLocation();
|
||||
|
||||
const { openFlyout, closeFlyout } = useExpandableFlyoutApi();
|
||||
|
@ -33,7 +30,7 @@ export const useUnifiedTableExpandableFlyout = ({
|
|||
|
||||
const isFlyoutOpen = useMemo(() => {
|
||||
/**
|
||||
* Currently, if new expanable flyout is closed, there is not way for
|
||||
* Currently, if new expandable flyout is closed, there is no way for
|
||||
* consumer to trigger an effect `onClose` of flyout. So, we are using
|
||||
* this hack to know if flyout is open or not.
|
||||
*
|
||||
|
@ -71,6 +68,5 @@ export const useUnifiedTableExpandableFlyout = ({
|
|||
isTimelineExpandableFlyoutOpen,
|
||||
openFlyout,
|
||||
closeFlyout: closeFlyoutWithEffect,
|
||||
isExpandableFlyoutDisabled: expandableFlyoutDisabled,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -33,109 +33,95 @@ import { mockRiskEngineEnabled } from '../../tasks/entity_analytics';
|
|||
const CURRENT_HOST_RISK_LEVEL = 'Current host risk level';
|
||||
const ORIGINAL_HOST_RISK_LEVEL = 'Original host risk level';
|
||||
|
||||
describe(
|
||||
'Enrichment',
|
||||
{
|
||||
tags: ['@ess'],
|
||||
env: {
|
||||
ftrConfig: {
|
||||
kbnServerArgs: [
|
||||
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
|
||||
'expandableFlyoutDisabled',
|
||||
])}`,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
() => {
|
||||
before(() => {
|
||||
cy.task('esArchiverUnload', { archiveName: 'risk_scores_new' });
|
||||
cy.task('esArchiverUnload', { archiveName: 'risk_scores_new_updated' });
|
||||
cy.task('esArchiverLoad', { archiveName: 'risk_users' });
|
||||
});
|
||||
// this whole suite is failing on main
|
||||
describe.skip('Enrichment', { tags: ['@ess'] }, () => {
|
||||
before(() => {
|
||||
cy.task('esArchiverUnload', { archiveName: 'risk_scores_new' });
|
||||
cy.task('esArchiverUnload', { archiveName: 'risk_scores_new_updated' });
|
||||
cy.task('esArchiverLoad', { archiveName: 'risk_users' });
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.task('esArchiverUnload', { archiveName: 'risk_users' });
|
||||
});
|
||||
after(() => {
|
||||
cy.task('esArchiverUnload', { archiveName: 'risk_users' });
|
||||
});
|
||||
|
||||
describe('Custom query rule', () => {
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/176965
|
||||
describe.skip('from legacy risk scores', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('esArchiverLoad', { archiveName: 'risk_hosts' });
|
||||
deleteAlertsAndRules();
|
||||
createRule(getNewRule({ rule_id: 'rule1' }));
|
||||
login();
|
||||
visitWithTimeRange(ALERTS_URL);
|
||||
waitForAlertsToPopulate();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.task('esArchiverUnload', { archiveName: 'risk_hosts' });
|
||||
cy.task('esArchiverUnload', { archiveName: 'risk_hosts_updated' });
|
||||
});
|
||||
|
||||
it('Should has enrichment fields from legacy risk', function () {
|
||||
cy.get(HOST_RISK_HEADER_COLUMN).contains('Host Risk Level');
|
||||
cy.get(USER_RISK_HEADER_COLUMN).contains('User Risk Level');
|
||||
scrollAlertTableColumnIntoView(HOST_RISK_COLUMN);
|
||||
cy.get(HOST_RISK_COLUMN).contains('Low');
|
||||
scrollAlertTableColumnIntoView(USER_RISK_COLUMN);
|
||||
cy.get(USER_RISK_COLUMN).contains('Low');
|
||||
scrollAlertTableColumnIntoView(ACTION_COLUMN);
|
||||
expandFirstAlert();
|
||||
cy.get(ENRICHED_DATA_ROW).contains('Low');
|
||||
cy.get(ENRICHED_DATA_ROW).contains(CURRENT_HOST_RISK_LEVEL);
|
||||
cy.get(ENRICHED_DATA_ROW).contains('Critical').should('not.exist');
|
||||
cy.get(ENRICHED_DATA_ROW).contains(ORIGINAL_HOST_RISK_LEVEL).should('not.exist');
|
||||
|
||||
closeAlertFlyout();
|
||||
cy.task('esArchiverUnload', { archiveName: 'risk_hosts' });
|
||||
cy.task('esArchiverLoad', { archiveName: 'risk_hosts_updated' });
|
||||
expandFirstAlert();
|
||||
cy.get(ENRICHED_DATA_ROW).contains('Critical');
|
||||
cy.get(ENRICHED_DATA_ROW).contains(ORIGINAL_HOST_RISK_LEVEL);
|
||||
});
|
||||
describe('Custom query rule', () => {
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/176965
|
||||
describe.skip('from legacy risk scores', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('esArchiverLoad', { archiveName: 'risk_hosts' });
|
||||
deleteAlertsAndRules();
|
||||
createRule(getNewRule({ rule_id: 'rule1' }));
|
||||
login();
|
||||
visitWithTimeRange(ALERTS_URL);
|
||||
waitForAlertsToPopulate();
|
||||
});
|
||||
|
||||
describe('from new risk scores', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('esArchiverLoad', { archiveName: 'risk_scores_new' });
|
||||
deleteAlertsAndRules();
|
||||
createRule(getNewRule({ rule_id: 'rule1' }));
|
||||
login();
|
||||
mockRiskEngineEnabled();
|
||||
visitWithTimeRange(ALERTS_URL);
|
||||
waitForAlertsToPopulate();
|
||||
});
|
||||
afterEach(() => {
|
||||
cy.task('esArchiverUnload', { archiveName: 'risk_hosts' });
|
||||
cy.task('esArchiverUnload', { archiveName: 'risk_hosts_updated' });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.task('esArchiverUnload', { archiveName: 'risk_scores_new' });
|
||||
cy.task('esArchiverUnload', { archiveName: 'risk_scores_new_updated' });
|
||||
});
|
||||
it('Should has enrichment fields from legacy risk', function () {
|
||||
cy.get(HOST_RISK_HEADER_COLUMN).contains('Host Risk Level');
|
||||
cy.get(USER_RISK_HEADER_COLUMN).contains('User Risk Level');
|
||||
scrollAlertTableColumnIntoView(HOST_RISK_COLUMN);
|
||||
cy.get(HOST_RISK_COLUMN).contains('Low');
|
||||
scrollAlertTableColumnIntoView(USER_RISK_COLUMN);
|
||||
cy.get(USER_RISK_COLUMN).contains('Low');
|
||||
scrollAlertTableColumnIntoView(ACTION_COLUMN);
|
||||
expandFirstAlert();
|
||||
cy.get(ENRICHED_DATA_ROW).contains('Low');
|
||||
cy.get(ENRICHED_DATA_ROW).contains(CURRENT_HOST_RISK_LEVEL);
|
||||
cy.get(ENRICHED_DATA_ROW).contains('Critical').should('not.exist');
|
||||
cy.get(ENRICHED_DATA_ROW).contains(ORIGINAL_HOST_RISK_LEVEL).should('not.exist');
|
||||
|
||||
it('Should has enrichment fields from legacy risk', function () {
|
||||
cy.get(HOST_RISK_HEADER_COLUMN).contains('Host Risk Level');
|
||||
cy.get(USER_RISK_HEADER_COLUMN).contains('User Risk Level');
|
||||
scrollAlertTableColumnIntoView(HOST_RISK_COLUMN);
|
||||
cy.get(HOST_RISK_COLUMN).contains('Critical');
|
||||
scrollAlertTableColumnIntoView(USER_RISK_COLUMN);
|
||||
cy.get(USER_RISK_COLUMN).contains('High');
|
||||
scrollAlertTableColumnIntoView(ACTION_COLUMN);
|
||||
expandFirstAlert();
|
||||
cy.get(ENRICHED_DATA_ROW).contains('Critical');
|
||||
cy.get(ENRICHED_DATA_ROW).contains(CURRENT_HOST_RISK_LEVEL);
|
||||
cy.get(ENRICHED_DATA_ROW).contains('Low').should('not.exist');
|
||||
cy.get(ENRICHED_DATA_ROW).contains(ORIGINAL_HOST_RISK_LEVEL).should('not.exist');
|
||||
|
||||
closeAlertFlyout();
|
||||
cy.task('esArchiverUnload', { archiveName: 'risk_scores_new' });
|
||||
cy.task('esArchiverLoad', { archiveName: 'risk_scores_new_updated' });
|
||||
expandFirstAlert();
|
||||
cy.get(ENRICHED_DATA_ROW).contains('Low');
|
||||
cy.get(ENRICHED_DATA_ROW).contains(ORIGINAL_HOST_RISK_LEVEL);
|
||||
});
|
||||
closeAlertFlyout();
|
||||
cy.task('esArchiverUnload', { archiveName: 'risk_hosts' });
|
||||
cy.task('esArchiverLoad', { archiveName: 'risk_hosts_updated' });
|
||||
expandFirstAlert();
|
||||
cy.get(ENRICHED_DATA_ROW).contains('Critical');
|
||||
cy.get(ENRICHED_DATA_ROW).contains(ORIGINAL_HOST_RISK_LEVEL);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
describe('from new risk scores', () => {
|
||||
beforeEach(() => {
|
||||
cy.task('esArchiverLoad', { archiveName: 'risk_scores_new' });
|
||||
deleteAlertsAndRules();
|
||||
createRule(getNewRule({ rule_id: 'rule1' }));
|
||||
login();
|
||||
mockRiskEngineEnabled();
|
||||
visitWithTimeRange(ALERTS_URL);
|
||||
waitForAlertsToPopulate();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.task('esArchiverUnload', { archiveName: 'risk_scores_new' });
|
||||
cy.task('esArchiverUnload', { archiveName: 'risk_scores_new_updated' });
|
||||
});
|
||||
|
||||
it('Should has enrichment fields from legacy risk', function () {
|
||||
cy.get(HOST_RISK_HEADER_COLUMN).contains('Host Risk Level');
|
||||
cy.get(USER_RISK_HEADER_COLUMN).contains('User Risk Level');
|
||||
scrollAlertTableColumnIntoView(HOST_RISK_COLUMN);
|
||||
cy.get(HOST_RISK_COLUMN).contains('Critical');
|
||||
scrollAlertTableColumnIntoView(USER_RISK_COLUMN);
|
||||
cy.get(USER_RISK_COLUMN).contains('High');
|
||||
scrollAlertTableColumnIntoView(ACTION_COLUMN);
|
||||
expandFirstAlert();
|
||||
cy.get(ENRICHED_DATA_ROW).contains('Critical');
|
||||
cy.get(ENRICHED_DATA_ROW).contains(CURRENT_HOST_RISK_LEVEL);
|
||||
cy.get(ENRICHED_DATA_ROW).contains('Low').should('not.exist');
|
||||
cy.get(ENRICHED_DATA_ROW).contains(ORIGINAL_HOST_RISK_LEVEL).should('not.exist');
|
||||
|
||||
closeAlertFlyout();
|
||||
cy.task('esArchiverUnload', { archiveName: 'risk_scores_new' });
|
||||
cy.task('esArchiverLoad', { archiveName: 'risk_scores_new_updated' });
|
||||
expandFirstAlert();
|
||||
cy.get(ENRICHED_DATA_ROW).contains('Low');
|
||||
cy.get(ENRICHED_DATA_ROW).contains(ORIGINAL_HOST_RISK_LEVEL);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,248 +0,0 @@
|
|||
/*
|
||||
* 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 {
|
||||
ALERT_FLYOUT,
|
||||
CELL_TEXT,
|
||||
COPY_ALERT_FLYOUT_LINK,
|
||||
JSON_TEXT,
|
||||
OVERVIEW_RULE,
|
||||
SUMMARY_VIEW,
|
||||
TABLE_CONTAINER,
|
||||
TABLE_ROWS,
|
||||
} from '../../../screens/alerts_details';
|
||||
import { closeAlertFlyout, expandFirstAlert } from '../../../tasks/alerts';
|
||||
import {
|
||||
changeAlertStatusTo,
|
||||
filterBy,
|
||||
openJsonView,
|
||||
openTable,
|
||||
} from '../../../tasks/alerts_details';
|
||||
import { createRule } from '../../../tasks/api_calls/rules';
|
||||
import { deleteAlertsAndRules } from '../../../tasks/api_calls/common';
|
||||
import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule';
|
||||
import { login } from '../../../tasks/login';
|
||||
import { visit, visitWithTimeRange } from '../../../tasks/navigation';
|
||||
import { getNewRule, getUnmappedRule } from '../../../objects/rule';
|
||||
import { ALERTS_URL } from '../../../urls/navigation';
|
||||
import { tablePageSelector } from '../../../screens/table_pagination';
|
||||
import { ALERTS_TABLE_COUNT } from '../../../screens/timeline';
|
||||
import { ALERT_SUMMARY_SEVERITY_DONUT_CHART } from '../../../screens/alerts';
|
||||
import {
|
||||
visitRuleDetailsPage,
|
||||
waitForPageToBeLoaded as waitForRuleDetailsPageToBeLoaded,
|
||||
} from '../../../tasks/rule_details';
|
||||
|
||||
// this functionality is now not used anymore (though still accessible if customers turn the `expandableFlyoutDisabled` feature flag on
|
||||
// the code will be removed entirely for `8.16 (see https://github.com/elastic/security-team/issues/7462)
|
||||
describe.skip('Alert details flyout', { tags: ['@ess', '@serverless'] }, () => {
|
||||
describe('Basic functions', () => {
|
||||
beforeEach(() => {
|
||||
deleteAlertsAndRules();
|
||||
createRule(getNewRule());
|
||||
login();
|
||||
visit(ALERTS_URL);
|
||||
waitForAlertsToPopulate();
|
||||
expandFirstAlert();
|
||||
});
|
||||
|
||||
it('should update the table when status of the alert is updated', () => {
|
||||
cy.get(OVERVIEW_RULE).should('be.visible');
|
||||
cy.get(ALERTS_TABLE_COUNT).should('have.text', '1 alert');
|
||||
cy.get(ALERT_SUMMARY_SEVERITY_DONUT_CHART).should('contain.text', '1alert');
|
||||
expandFirstAlert();
|
||||
changeAlertStatusTo('acknowledged');
|
||||
cy.get(ALERTS_TABLE_COUNT).should('have.text', '1 alert');
|
||||
cy.get(ALERT_SUMMARY_SEVERITY_DONUT_CHART).should('contain.text', '1alert');
|
||||
});
|
||||
});
|
||||
|
||||
describe('With unmapped fields', () => {
|
||||
before(() => {
|
||||
cy.task('esArchiverLoad', { archiveName: 'unmapped_fields' });
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
deleteAlertsAndRules();
|
||||
createRule({ ...getUnmappedRule(), investigation_fields: { field_names: ['event.kind'] } });
|
||||
login();
|
||||
visit(ALERTS_URL);
|
||||
waitForAlertsToPopulate();
|
||||
expandFirstAlert();
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.task('esArchiverUnload', { archiveName: 'unmapped_fields' });
|
||||
});
|
||||
|
||||
it.skip('should display user and system defined highlighted fields', () => {
|
||||
cy.get(SUMMARY_VIEW)
|
||||
.should('be.visible')
|
||||
.and('contain.text', 'event.kind')
|
||||
.and('contain.text', 'Rule type');
|
||||
});
|
||||
|
||||
it('should display the unmapped field on the JSON view', () => {
|
||||
const expectedUnmappedValue = 'This is the unmapped field';
|
||||
|
||||
openJsonView();
|
||||
|
||||
cy.get(JSON_TEXT).then((x) => {
|
||||
const parsed = JSON.parse(x.text());
|
||||
expect(parsed.fields.unmapped[0]).to.equal(expectedUnmappedValue);
|
||||
});
|
||||
});
|
||||
|
||||
it('should displays the unmapped field on the table', () => {
|
||||
const expectedUnmappedField = {
|
||||
field: 'unmapped',
|
||||
text: 'This is the unmapped field',
|
||||
};
|
||||
|
||||
openTable();
|
||||
cy.get(ALERT_FLYOUT).find(tablePageSelector(6)).click({ force: true });
|
||||
cy.get(ALERT_FLYOUT)
|
||||
.find(TABLE_ROWS)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get(CELL_TEXT).should('contain', expectedUnmappedField.field);
|
||||
cy.get(CELL_TEXT).should('contain', expectedUnmappedField.text);
|
||||
});
|
||||
});
|
||||
|
||||
// This test makes sure that the table does not overflow horizontally
|
||||
it('table should not scroll horizontally', () => {
|
||||
openTable();
|
||||
|
||||
cy.get(ALERT_FLYOUT)
|
||||
.find(TABLE_CONTAINER)
|
||||
.within(($tableContainer) => {
|
||||
expect($tableContainer[0].scrollLeft).to.equal(0);
|
||||
|
||||
// Due to the introduction of pagination on the table, a slight horizontal overflow has been introduced.
|
||||
// scroll ignores the `overflow-x:hidden` attribute and will still scroll the element if there is a hidden overflow
|
||||
// Updated the below to < 5 to account for this and keep a test to make sure it doesn't grow
|
||||
$tableContainer[0].scroll({ left: 1000 });
|
||||
|
||||
expect($tableContainer[0].scrollLeft).to.be.lessThan(5);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Url state management', () => {
|
||||
before(() => {
|
||||
cy.task('esArchiverLoad', { archiveName: 'query_alert', useCreate: true, docsOnly: true });
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
deleteAlertsAndRules();
|
||||
createRule(getNewRule());
|
||||
login();
|
||||
visitWithTimeRange(ALERTS_URL);
|
||||
waitForAlertsToPopulate();
|
||||
expandFirstAlert();
|
||||
});
|
||||
|
||||
it('should store the flyout state in the url when it is opened and remove it when closed', () => {
|
||||
cy.get(OVERVIEW_RULE).should('be.visible');
|
||||
cy.url().should('include', 'flyout=');
|
||||
|
||||
closeAlertFlyout();
|
||||
|
||||
cy.url().should('not.include', 'flyout=');
|
||||
});
|
||||
|
||||
it.skip('should open the alert flyout when the page is refreshed', () => {
|
||||
cy.get(OVERVIEW_RULE).should('be.visible');
|
||||
cy.reload();
|
||||
cy.get(OVERVIEW_RULE).should('be.visible');
|
||||
cy.get(OVERVIEW_RULE).should('contain', 'Endpoint Security');
|
||||
});
|
||||
|
||||
it('should show the copy link button for the flyout', () => {
|
||||
cy.get(COPY_ALERT_FLYOUT_LINK).should('be.visible');
|
||||
});
|
||||
|
||||
it.skip('should have the `kibana.alert.url` field set', () => {
|
||||
openTable();
|
||||
filterBy('kibana.alert.url');
|
||||
cy.get('[data-test-subj="formatted-field-kibana.alert.url"]').should(
|
||||
'have.text',
|
||||
'http://localhost:5601/app/security/alerts/redirect/eabbdefc23da981f2b74ab58b82622a97bb9878caa11bc914e2adfacc94780f1?index=.alerts-security.alerts-default×tamp=2023-04-27T11:03:57.906Z'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Localstorage management', () => {
|
||||
const ARCHIVED_RULE_ID = '7015a3e2-e4ea-11ed-8c11-49608884878f';
|
||||
const ARCHIVED_RULE_NAME = 'Endpoint Security';
|
||||
|
||||
before(() => {
|
||||
deleteAlertsAndRules();
|
||||
// It just imports an alert without a rule but rule details page should work anyway
|
||||
cy.task('esArchiverLoad', { archiveName: 'query_alert', useCreate: true, docsOnly: true });
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.task('esArchiverUnload', { archiveName: 'query_alert' });
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
createRule(getNewRule());
|
||||
login();
|
||||
visitWithTimeRange(ALERTS_URL);
|
||||
waitForAlertsToPopulate();
|
||||
expandFirstAlert();
|
||||
});
|
||||
|
||||
/**
|
||||
* Localstorage is updated after a delay here x-pack/plugins/security_solution/public/common/store/data_table/epic_local_storage.ts
|
||||
* We create this config to re-check localStorage 3 times, every 500ms to avoid any potential flakyness from that delay
|
||||
*/
|
||||
|
||||
it('should store the flyout state in localstorage', () => {
|
||||
cy.get(OVERVIEW_RULE).should('be.visible');
|
||||
|
||||
cy.window().then((win) => {
|
||||
const securityDataTableStorage = win.localStorage.getItem('securityDataTable');
|
||||
cy.wrap(securityDataTableStorage).should('contain', '"panelView":"eventDetail"');
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove the flyout details from local storage when closed', () => {
|
||||
cy.get(OVERVIEW_RULE).should('be.visible');
|
||||
closeAlertFlyout();
|
||||
|
||||
cy.window().then((win) => {
|
||||
const securityDataTableStorage = win.localStorage.getItem('securityDataTable');
|
||||
cy.wrap(securityDataTableStorage).should('not.contain', '"panelView"');
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove the flyout state from localstorage when navigating away without closing the flyout', () => {
|
||||
cy.get(OVERVIEW_RULE).should('be.visible');
|
||||
|
||||
visitRuleDetailsPage(ARCHIVED_RULE_ID);
|
||||
waitForRuleDetailsPageToBeLoaded(ARCHIVED_RULE_NAME);
|
||||
|
||||
cy.window().then((win) => {
|
||||
const securityDataTableStorage = win.localStorage.getItem('securityDataTable');
|
||||
cy.wrap(securityDataTableStorage).should('not.contain', '"panelView"');
|
||||
});
|
||||
});
|
||||
|
||||
it('should not reopen the flyout when navigating away from the alerts page and returning to it', () => {
|
||||
cy.get(OVERVIEW_RULE).should('be.visible');
|
||||
|
||||
visitRuleDetailsPage(ARCHIVED_RULE_ID);
|
||||
waitForRuleDetailsPageToBeLoaded(ARCHIVED_RULE_NAME);
|
||||
|
||||
visit(ALERTS_URL);
|
||||
cy.get(OVERVIEW_RULE).should('not.exist');
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue