mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
# Backport This will backport the following commits from `main` to `8.x`: - [[Security Solution] Convert isolate host to standalone flyout (#211853)](https://github.com/elastic/kibana/pull/211853) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"christineweng","email":"18648970+christineweng@users.noreply.github.com"},"sourceCommit":{"committedDate":"2025-02-25T18:11:36Z","message":"[Security Solution] Convert isolate host to standalone flyout (#211853)\n\n## Summary\n\nRef: https://github.com/elastic/kibana/issues/207596\n\nThis PR fixed a bug where when user has a alert open in preview, then\nclicks isolate/release host, the panel opens in the background. This is\nbecause the isolate host panel was called via `openRightPanel`, which\nonly replaces the panel and not opens a new flyout. To make the isolate\nhost flyout consistent with other actions, this PR converts the panel\ninto a normal EUI flyout.\n\n\nhttps://github.com/user-attachments/assets/7da4baa0-61ee-4166-9ff1-57c1078a1547\n\n\n**How to test**\n- Apply license (platinum+)\n- To enable the isolate/release host button, you can use this command\n`yarn test:generate --fleet --withNewUser=test:changeme`\n\n\n### Checklist\n\n- [ ] Any text added follows [EUI's writing\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\nsentence case text and includes [i18n\nsupport](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)\n- [x] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios\n- [x] The PR description includes the appropriate Release Notes section,\nand the correct `release_note:*` label is applied per the\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)","sha":"74e132027617cbeef8a41450626065a884505068","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","v9.0.0","Team:Threat Hunting:Investigations","backport:version","v8.18.0","v9.1.0","v8.19.0"],"title":"[Security Solution] Convert isolate host to standalone flyout","number":211853,"url":"https://github.com/elastic/kibana/pull/211853","mergeCommit":{"message":"[Security Solution] Convert isolate host to standalone flyout (#211853)\n\n## Summary\n\nRef: https://github.com/elastic/kibana/issues/207596\n\nThis PR fixed a bug where when user has a alert open in preview, then\nclicks isolate/release host, the panel opens in the background. This is\nbecause the isolate host panel was called via `openRightPanel`, which\nonly replaces the panel and not opens a new flyout. To make the isolate\nhost flyout consistent with other actions, this PR converts the panel\ninto a normal EUI flyout.\n\n\nhttps://github.com/user-attachments/assets/7da4baa0-61ee-4166-9ff1-57c1078a1547\n\n\n**How to test**\n- Apply license (platinum+)\n- To enable the isolate/release host button, you can use this command\n`yarn test:generate --fleet --withNewUser=test:changeme`\n\n\n### Checklist\n\n- [ ] Any text added follows [EUI's writing\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\nsentence case text and includes [i18n\nsupport](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)\n- [x] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios\n- [x] The PR description includes the appropriate Release Notes section,\nand the correct `release_note:*` label is applied per the\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)","sha":"74e132027617cbeef8a41450626065a884505068"}},"sourceBranch":"main","suggestedTargetBranches":["9.0","8.18","8.x"],"targetPullRequestStates":[{"branch":"9.0","label":"v9.0.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.18","label":"v8.18.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/211853","number":211853,"mergeCommit":{"message":"[Security Solution] Convert isolate host to standalone flyout (#211853)\n\n## Summary\n\nRef: https://github.com/elastic/kibana/issues/207596\n\nThis PR fixed a bug where when user has a alert open in preview, then\nclicks isolate/release host, the panel opens in the background. This is\nbecause the isolate host panel was called via `openRightPanel`, which\nonly replaces the panel and not opens a new flyout. To make the isolate\nhost flyout consistent with other actions, this PR converts the panel\ninto a normal EUI flyout.\n\n\nhttps://github.com/user-attachments/assets/7da4baa0-61ee-4166-9ff1-57c1078a1547\n\n\n**How to test**\n- Apply license (platinum+)\n- To enable the isolate/release host button, you can use this command\n`yarn test:generate --fleet --withNewUser=test:changeme`\n\n\n### Checklist\n\n- [ ] Any text added follows [EUI's writing\nguidelines](https://elastic.github.io/eui/#/guidelines/writing), uses\nsentence case text and includes [i18n\nsupport](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md)\n- [x] [Unit or functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere updated or added to match the most common scenarios\n- [x] The PR description includes the appropriate Release Notes section,\nand the correct `release_note:*` label is applied per the\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)","sha":"74e132027617cbeef8a41450626065a884505068"}},{"branch":"8.x","label":"v8.19.0","branchLabelMappingKey":"^v8.19.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT--> Co-authored-by: christineweng <18648970+christineweng@users.noreply.github.com>
This commit is contained in:
parent
c7cf3683ee
commit
6cdb590f63
7 changed files with 220 additions and 39 deletions
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import type { AppContextTestRender } from '../../../common/mock/endpoint';
|
||||
import { createAppRootMockRenderer, endpointAlertDataMock } from '../../../common/mock/endpoint';
|
||||
import { FLYOUT_HOST_ISOLATION_PANEL_TEST_ID } from './test_ids';
|
||||
import { IsolateHostPanelContent } from './content';
|
||||
|
||||
describe('<IsolateHostPanelContent />', () => {
|
||||
let appContextMock: AppContextTestRender;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
appContextMock = createAppRootMockRenderer();
|
||||
|
||||
appContextMock.setExperimentalFlag({
|
||||
responseActionsSentinelOneV1Enabled: true,
|
||||
responseActionsCrowdstrikeManualHostIsolationEnabled: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should display content with success banner when action is isolateHost', () => {
|
||||
const { getByTestId } = appContextMock.render(
|
||||
<IsolateHostPanelContent
|
||||
isIsolateActionSuccessBannerVisible={true}
|
||||
hostName="some-host-name"
|
||||
alertId="some-alert-id"
|
||||
isolateAction="isolateHost"
|
||||
dataFormattedForFieldBrowser={endpointAlertDataMock.generateEndpointAlertDetailsItemData()}
|
||||
showAlertDetails={() => {}}
|
||||
handleIsolationActionSuccess={() => {}}
|
||||
/>
|
||||
);
|
||||
expect(getByTestId(FLYOUT_HOST_ISOLATION_PANEL_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId('hostIsolateSuccessMessage')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display content without success banner when action is isolateHost and success banner is not visible', () => {
|
||||
const { getByTestId, queryByTestId } = appContextMock.render(
|
||||
<IsolateHostPanelContent
|
||||
isIsolateActionSuccessBannerVisible={false}
|
||||
hostName="some-host-name"
|
||||
alertId="some-alert-id"
|
||||
isolateAction="isolateHost"
|
||||
dataFormattedForFieldBrowser={endpointAlertDataMock.generateEndpointAlertDetailsItemData()}
|
||||
showAlertDetails={() => {}}
|
||||
handleIsolationActionSuccess={() => {}}
|
||||
/>
|
||||
);
|
||||
expect(getByTestId(FLYOUT_HOST_ISOLATION_PANEL_TEST_ID)).toBeInTheDocument();
|
||||
expect(queryByTestId('hostIsolateSuccessMessage')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display content with success banner when action is unisolateHost', () => {
|
||||
const { getByTestId } = appContextMock.render(
|
||||
<IsolateHostPanelContent
|
||||
isIsolateActionSuccessBannerVisible={true}
|
||||
hostName="some-host-name"
|
||||
alertId="some-alert-id"
|
||||
isolateAction="unisolateHost"
|
||||
dataFormattedForFieldBrowser={endpointAlertDataMock.generateEndpointAlertDetailsItemData()}
|
||||
showAlertDetails={() => {}}
|
||||
handleIsolationActionSuccess={() => {}}
|
||||
/>
|
||||
);
|
||||
expect(getByTestId(FLYOUT_HOST_ISOLATION_PANEL_TEST_ID)).toBeInTheDocument();
|
||||
expect(getByTestId('hostUnisolateSuccessMessage')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display content without success banner when action is unisolateHost', () => {
|
||||
const { getByTestId, queryByTestId } = appContextMock.render(
|
||||
<IsolateHostPanelContent
|
||||
isIsolateActionSuccessBannerVisible={false}
|
||||
hostName="some-host-name"
|
||||
alertId="some-alert-id"
|
||||
isolateAction="unisolateHost"
|
||||
dataFormattedForFieldBrowser={endpointAlertDataMock.generateEndpointAlertDetailsItemData()}
|
||||
showAlertDetails={() => {}}
|
||||
handleIsolationActionSuccess={() => {}}
|
||||
/>
|
||||
);
|
||||
expect(getByTestId(FLYOUT_HOST_ISOLATION_PANEL_TEST_ID)).toBeInTheDocument();
|
||||
expect(queryByTestId('hostUnisolateSuccessMessage')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
|
@ -8,6 +8,7 @@
|
|||
import type { FC } from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
|
||||
import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
|
||||
import { DocumentDetailsRightPanelKey } from '../shared/constants/panel_keys';
|
||||
import { useBasicDataFromDetailsData } from '../shared/hooks/use_basic_data_from_details_data';
|
||||
import {
|
||||
|
@ -17,6 +18,7 @@ import {
|
|||
import { useHostIsolation } from '../shared/hooks/use_host_isolation';
|
||||
import { useIsolateHostPanelContext } from './context';
|
||||
import { FlyoutBody } from '../../shared/components/flyout_body';
|
||||
import { FLYOUT_HOST_ISOLATION_PANEL_TEST_ID } from './test_ids';
|
||||
|
||||
/**
|
||||
* Document details expandable flyout section content for the isolate host component, displaying the form or the success banner
|
||||
|
@ -44,7 +46,36 @@ export const PanelContent: FC = () => {
|
|||
);
|
||||
|
||||
return (
|
||||
<FlyoutBody>
|
||||
<IsolateHostPanelContent
|
||||
isIsolateActionSuccessBannerVisible={isIsolateActionSuccessBannerVisible}
|
||||
hostName={hostName}
|
||||
alertId={alertId}
|
||||
isolateAction={isolateAction}
|
||||
dataFormattedForFieldBrowser={dataFormattedForFieldBrowser}
|
||||
showAlertDetails={showAlertDetails}
|
||||
handleIsolationActionSuccess={handleIsolationActionSuccess}
|
||||
/>
|
||||
);
|
||||
};
|
||||
export const IsolateHostPanelContent: FC<{
|
||||
isIsolateActionSuccessBannerVisible: boolean;
|
||||
hostName: string;
|
||||
alertId: string;
|
||||
isolateAction: 'isolateHost' | 'unisolateHost';
|
||||
dataFormattedForFieldBrowser: TimelineEventsDetailsItem[];
|
||||
showAlertDetails: () => void;
|
||||
handleIsolationActionSuccess: () => void;
|
||||
}> = ({
|
||||
isIsolateActionSuccessBannerVisible,
|
||||
hostName,
|
||||
alertId,
|
||||
isolateAction,
|
||||
dataFormattedForFieldBrowser,
|
||||
showAlertDetails,
|
||||
handleIsolationActionSuccess,
|
||||
}) => {
|
||||
return (
|
||||
<FlyoutBody data-test-subj={FLYOUT_HOST_ISOLATION_PANEL_TEST_ID}>
|
||||
{isIsolateActionSuccessBannerVisible && (
|
||||
<EndpointIsolateSuccess
|
||||
hostName={hostName}
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import type { IsolateHostPanelContext } from './context';
|
||||
import { useIsolateHostPanelContext } from './context';
|
||||
import { PanelHeader } from './header';
|
||||
import { PanelHeader, IsolateHostPanelHeader } from './header';
|
||||
import type { AppContextTestRender } from '../../../common/mock/endpoint';
|
||||
import { createAppRootMockRenderer, endpointAlertDataMock } from '../../../common/mock/endpoint';
|
||||
import type { ResponseActionAgentType } from '../../../../common/endpoint/service/response_actions/constants';
|
||||
|
@ -18,11 +19,11 @@ import { ISOLATE_HOST, UNISOLATE_HOST } from '../../../common/components/endpoin
|
|||
jest.mock('./context');
|
||||
|
||||
describe('Isolation Flyout PanelHeader', () => {
|
||||
let render: () => ReturnType<AppContextTestRender['render']>;
|
||||
let renderComponent: () => ReturnType<AppContextTestRender['render']>;
|
||||
|
||||
const setUseIsolateHostPanelContext = (data: Partial<IsolateHostPanelContext> = {}) => {
|
||||
const panelContextMock: IsolateHostPanelContext = {
|
||||
eventId: 'some-even-1',
|
||||
eventId: 'some-event-1',
|
||||
indexName: 'some-index-name',
|
||||
scopeId: 'some-scope-id',
|
||||
dataFormattedForFieldBrowser: endpointAlertDataMock.generateEndpointAlertDetailsItemData(),
|
||||
|
@ -41,7 +42,7 @@ describe('Isolation Flyout PanelHeader', () => {
|
|||
responseActionsCrowdstrikeManualHostIsolationEnabled: true,
|
||||
});
|
||||
|
||||
render = () => appContextMock.render(<PanelHeader />);
|
||||
renderComponent = () => appContextMock.render(<PanelHeader />);
|
||||
|
||||
setUseIsolateHostPanelContext({
|
||||
isolateAction: 'isolateHost',
|
||||
|
@ -79,10 +80,23 @@ describe('Isolation Flyout PanelHeader', () => {
|
|||
dataFormattedForFieldBrowser:
|
||||
endpointAlertDataMock.generateAlertDetailsItemDataForAgentType(agentType),
|
||||
});
|
||||
const { getByTestId } = render();
|
||||
const { getByTestId } = renderComponent();
|
||||
|
||||
expect(getByTestId('flyoutHostIsolationHeaderTitle')).toHaveTextContent(title);
|
||||
expect(getByTestId('flyoutHostIsolationHeaderIntegration'));
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('<IsolateHostPanelHeader />', () => {
|
||||
it('should display correct flyout header title for isolateHost', () => {
|
||||
const { getByTestId } = render(
|
||||
<IsolateHostPanelHeader
|
||||
isolateAction="isolateHost"
|
||||
data={endpointAlertDataMock.generateEndpointAlertDetailsItemData()}
|
||||
/>
|
||||
);
|
||||
expect(getByTestId('flyoutHostIsolationHeaderTitle')).toHaveTextContent(ISOLATE_HOST);
|
||||
expect(getByTestId('flyoutHostIsolationHeaderIntegration')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui';
|
||||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
|
||||
import { AgentTypeIntegration } from '../../../common/components/endpoint/agents/agent_type_integration';
|
||||
import { useAlertResponseActionsSupport } from '../../../common/hooks/endpoint/use_alert_response_actions_support';
|
||||
import { useIsolateHostPanelContext } from './context';
|
||||
|
@ -20,6 +21,13 @@ import { ISOLATE_HOST, UNISOLATE_HOST } from '../../../common/components/endpoin
|
|||
*/
|
||||
export const PanelHeader: FC = () => {
|
||||
const { isolateAction, dataFormattedForFieldBrowser: data } = useIsolateHostPanelContext();
|
||||
return <IsolateHostPanelHeader isolateAction={isolateAction} data={data} />;
|
||||
};
|
||||
|
||||
export const IsolateHostPanelHeader: FC<{
|
||||
isolateAction: string;
|
||||
data: TimelineEventsDetailsItem[];
|
||||
}> = ({ isolateAction, data }) => {
|
||||
const {
|
||||
details: { agentType },
|
||||
} = useAlertResponseActionsSupport(data);
|
||||
|
|
|
@ -8,3 +8,4 @@
|
|||
import { PREFIX } from '../../shared/test_ids';
|
||||
|
||||
export const FLYOUT_HEADER_TITLE_TEST_ID = `${PREFIX}HeaderTitle` as const;
|
||||
export const FLYOUT_HOST_ISOLATION_PANEL_TEST_ID = `${PREFIX}HostIsolationPanel` as const;
|
||||
|
|
|
@ -8,8 +8,9 @@
|
|||
import type { FC } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
|
||||
import { useEuiTheme } from '@elastic/eui';
|
||||
import { useEuiTheme, EuiFlyout } from '@elastic/eui';
|
||||
import { find } from 'lodash/fp';
|
||||
import { useBasicDataFromDetailsData } from '../hooks/use_basic_data_from_details_data';
|
||||
import type { Status } from '../../../../../common/api/detection_engine';
|
||||
import { getAlertDetailsFieldValue } from '../../../../common/lib/endpoint/utils/get_event_details_field_values';
|
||||
import { TakeActionDropdown } from './take_action_dropdown';
|
||||
|
@ -18,12 +19,12 @@ import { EventFiltersFlyout } from '../../../../management/pages/event_filters/v
|
|||
import { OsqueryFlyout } from '../../../../detections/components/osquery/osquery_flyout';
|
||||
import { useDocumentDetailsContext } from '../context';
|
||||
import { useHostIsolation } from '../hooks/use_host_isolation';
|
||||
import { DocumentDetailsIsolateHostPanelKey } from '../constants/panel_keys';
|
||||
import { useRefetchByScope } from '../../right/hooks/use_refetch_by_scope';
|
||||
import { useExceptionFlyout } from '../../../../detections/components/alerts_table/timeline_actions/use_add_exception_flyout';
|
||||
import { isActiveTimeline } from '../../../../helpers';
|
||||
import { useEventFilterModal } from '../../../../detections/components/alerts_table/timeline_actions/use_event_filter_modal';
|
||||
|
||||
import { IsolateHostPanelHeader } from '../../isolate_host/header';
|
||||
import { IsolateHostPanelContent } from '../../isolate_host/content';
|
||||
interface AlertSummaryData {
|
||||
/**
|
||||
* Status of the alert (open, closed...)
|
||||
|
@ -58,33 +59,21 @@ export const TakeActionButton: FC = () => {
|
|||
[euiTheme]
|
||||
);
|
||||
|
||||
const { closeFlyout, openRightPanel } = useExpandableFlyoutApi();
|
||||
const {
|
||||
eventId,
|
||||
indexName,
|
||||
dataFormattedForFieldBrowser,
|
||||
dataAsNestedObject,
|
||||
refetchFlyoutData,
|
||||
scopeId,
|
||||
} = useDocumentDetailsContext();
|
||||
const { closeFlyout } = useExpandableFlyoutApi();
|
||||
const { dataFormattedForFieldBrowser, dataAsNestedObject, refetchFlyoutData, scopeId } =
|
||||
useDocumentDetailsContext();
|
||||
|
||||
// host isolation interaction
|
||||
const { isHostIsolationPanelOpen, showHostIsolationPanel } = useHostIsolation();
|
||||
const showHostIsolationPanelCallback = useCallback(
|
||||
(action: 'isolateHost' | 'unisolateHost' | undefined) => {
|
||||
showHostIsolationPanel(action);
|
||||
openRightPanel({
|
||||
id: DocumentDetailsIsolateHostPanelKey,
|
||||
params: {
|
||||
id: eventId,
|
||||
indexName,
|
||||
scopeId,
|
||||
isolateAction: action,
|
||||
},
|
||||
});
|
||||
},
|
||||
[eventId, indexName, openRightPanel, scopeId, showHostIsolationPanel]
|
||||
);
|
||||
const {
|
||||
isolateAction,
|
||||
isHostIsolationPanelOpen,
|
||||
showHostIsolationPanel,
|
||||
isIsolateActionSuccessBannerVisible,
|
||||
handleIsolationActionSuccess,
|
||||
showAlertDetails,
|
||||
} = useHostIsolation();
|
||||
|
||||
const { hostName } = useBasicDataFromDetailsData(dataFormattedForFieldBrowser);
|
||||
|
||||
const { refetch: refetchAll } = useRefetchByScope({ scopeId });
|
||||
|
||||
|
@ -174,7 +163,7 @@ export const TakeActionButton: FC = () => {
|
|||
isHostIsolationPanelOpen={isHostIsolationPanelOpen}
|
||||
onAddEventFilterClick={onAddEventFilterClick}
|
||||
onAddExceptionTypeClick={onAddExceptionTypeClick}
|
||||
onAddIsolationStatusClick={showHostIsolationPanelCallback}
|
||||
onAddIsolationStatusClick={showHostIsolationPanel}
|
||||
refetchFlyoutData={refetchFlyoutData}
|
||||
refetch={refetchAll}
|
||||
scopeId={scopeId}
|
||||
|
@ -213,6 +202,25 @@ export const TakeActionButton: FC = () => {
|
|||
ecsData={dataAsNestedObject}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isHostIsolationPanelOpen && alertId && (
|
||||
// EUI TODO: This z-index override of EuiOverlayMask is a workaround, and ideally should be resolved with a cleaner UI/UX flow long-term
|
||||
<EuiFlyout onClose={showAlertDetails} size="m" maskProps={flyoutZIndex}>
|
||||
<IsolateHostPanelHeader
|
||||
isolateAction={isolateAction}
|
||||
data={dataFormattedForFieldBrowser}
|
||||
/>
|
||||
<IsolateHostPanelContent
|
||||
isIsolateActionSuccessBannerVisible={isIsolateActionSuccessBannerVisible}
|
||||
hostName={hostName}
|
||||
alertId={alertId}
|
||||
isolateAction={isolateAction}
|
||||
dataFormattedForFieldBrowser={dataFormattedForFieldBrowser}
|
||||
showAlertDetails={showAlertDetails}
|
||||
handleIsolationActionSuccess={handleIsolationActionSuccess}
|
||||
/>
|
||||
</EuiFlyout>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -9,11 +9,13 @@ import { useCallback, useMemo, useReducer } from 'react';
|
|||
import { useWithCaseDetailsRefresh } from '../../../../common/components/endpoint';
|
||||
|
||||
interface State {
|
||||
isolateAction: 'isolateHost' | 'unisolateHost';
|
||||
isHostIsolationPanelOpen: boolean;
|
||||
isIsolateActionSuccessBannerVisible: boolean;
|
||||
}
|
||||
|
||||
const initialState: State = {
|
||||
isolateAction: 'isolateHost',
|
||||
isHostIsolationPanelOpen: false,
|
||||
isIsolateActionSuccessBannerVisible: false,
|
||||
};
|
||||
|
@ -23,6 +25,10 @@ type HostIsolationActions =
|
|||
type: 'setIsHostIsolationPanel';
|
||||
isHostIsolationPanelOpen: boolean;
|
||||
}
|
||||
| {
|
||||
type: 'setIsolateAction';
|
||||
isolateAction: 'isolateHost' | 'unisolateHost';
|
||||
}
|
||||
| {
|
||||
type: 'setIsIsolateActionSuccessBannerVisible';
|
||||
isIsolateActionSuccessBannerVisible: boolean;
|
||||
|
@ -30,6 +36,8 @@ type HostIsolationActions =
|
|||
|
||||
function reducer(state: State, action: HostIsolationActions) {
|
||||
switch (action.type) {
|
||||
case 'setIsolateAction':
|
||||
return { ...state, isolateAction: action.isolateAction };
|
||||
case 'setIsHostIsolationPanel':
|
||||
return { ...state, isHostIsolationPanelOpen: action.isHostIsolationPanelOpen };
|
||||
case 'setIsIsolateActionSuccessBannerVisible':
|
||||
|
@ -43,6 +51,10 @@ function reducer(state: State, action: HostIsolationActions) {
|
|||
}
|
||||
|
||||
export interface UseHostIsolationResult {
|
||||
/**
|
||||
* The action to take on the host
|
||||
*/
|
||||
isolateAction: 'isolateHost' | 'unisolateHost';
|
||||
/**
|
||||
* True if the host isolation panel is open in the flyout
|
||||
*/
|
||||
|
@ -59,21 +71,34 @@ export interface UseHostIsolationResult {
|
|||
* Callback to show the host isolation panel in the flyout
|
||||
*/
|
||||
showHostIsolationPanel: (action: 'isolateHost' | 'unisolateHost' | undefined) => void;
|
||||
/**
|
||||
* Callback to show the alert details in the flyout
|
||||
*/
|
||||
showAlertDetails: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook that returns the information for a parent to render the host isolation panel in the flyout
|
||||
*/
|
||||
export const useHostIsolation = (): UseHostIsolationResult => {
|
||||
const [{ isHostIsolationPanelOpen, isIsolateActionSuccessBannerVisible }, dispatch] = useReducer(
|
||||
reducer,
|
||||
initialState
|
||||
);
|
||||
const [
|
||||
{ isolateAction, isHostIsolationPanelOpen, isIsolateActionSuccessBannerVisible },
|
||||
dispatch,
|
||||
] = useReducer(reducer, initialState);
|
||||
|
||||
const showAlertDetails = useCallback(() => {
|
||||
dispatch({ type: 'setIsHostIsolationPanel', isHostIsolationPanelOpen: false });
|
||||
dispatch({
|
||||
type: 'setIsIsolateActionSuccessBannerVisible',
|
||||
isIsolateActionSuccessBannerVisible: false,
|
||||
});
|
||||
}, []);
|
||||
|
||||
const showHostIsolationPanel = useCallback(
|
||||
(action: 'isolateHost' | 'unisolateHost' | undefined) => {
|
||||
if (action === 'isolateHost' || action === 'unisolateHost') {
|
||||
dispatch({ type: 'setIsHostIsolationPanel', isHostIsolationPanelOpen: true });
|
||||
dispatch({ type: 'setIsolateAction', isolateAction: action });
|
||||
}
|
||||
},
|
||||
[]
|
||||
|
@ -95,15 +120,19 @@ export const useHostIsolation = (): UseHostIsolationResult => {
|
|||
|
||||
return useMemo(
|
||||
() => ({
|
||||
isolateAction,
|
||||
isHostIsolationPanelOpen,
|
||||
isIsolateActionSuccessBannerVisible,
|
||||
handleIsolationActionSuccess,
|
||||
showAlertDetails,
|
||||
showHostIsolationPanel,
|
||||
}),
|
||||
[
|
||||
isolateAction,
|
||||
isHostIsolationPanelOpen,
|
||||
isIsolateActionSuccessBannerVisible,
|
||||
handleIsolationActionSuccess,
|
||||
showAlertDetails,
|
||||
showHostIsolationPanel,
|
||||
]
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue