Skip to content

Commit b0700a8

Browse files
[Security Solution] restyle endpoint details flyout (#102092) (#102221)
Co-authored-by: Joey F. Poon <joey.poon@elastic.co>
1 parent 76b1040 commit b0700a8

8 files changed

Lines changed: 79 additions & 163 deletions

File tree

x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/components/actions_menu.test.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ describe('When using the Endpoint Details Actions Menu', () => {
7070
['View host details', 'hostLink'],
7171
['View agent policy', 'agentPolicyLink'],
7272
['View agent details', 'agentDetailsLink'],
73+
['Reassign agent policy', 'agentPolicyReassignLink'],
7374
])('should display %s action', async (_, dataTestSubj) => {
7475
await render();
7576
expect(renderResult.getByTestId(dataTestSubj)).not.toBeNull();
@@ -80,6 +81,7 @@ describe('When using the Endpoint Details Actions Menu', () => {
8081
['View host details', 'hostLink'],
8182
['View agent policy', 'agentPolicyLink'],
8283
['View agent details', 'agentDetailsLink'],
84+
['Reassign agent policy', 'agentPolicyReassignLink'],
8385
])(
8486
'should navigate via kibana `navigateToApp()` when %s is clicked',
8587
async (_, dataTestSubj) => {

x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/endpoint_details.tsx

Lines changed: 42 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,8 @@
88
import styled from 'styled-components';
99
import {
1010
EuiDescriptionList,
11-
EuiHorizontalRule,
1211
EuiListGroup,
1312
EuiListGroupItem,
14-
EuiIcon,
1513
EuiText,
1614
EuiFlexGroup,
1715
EuiFlexItem,
@@ -23,17 +21,14 @@ import { FormattedMessage } from '@kbn/i18n/react';
2321
import { i18n } from '@kbn/i18n';
2422
import { isPolicyOutOfDate } from '../../utils';
2523
import { HostInfo, HostMetadata, HostStatus } from '../../../../../../common/endpoint/types';
26-
import { useEndpointSelector, useAgentDetailsIngestUrl } from '../hooks';
27-
import { useNavigateToAppEventHandler } from '../../../../../common/hooks/endpoint/use_navigate_to_app_event_handler';
24+
import { useEndpointSelector } from '../hooks';
2825
import { policyResponseStatus, uiQueryParams } from '../../store/selectors';
2926
import { POLICY_STATUS_TO_BADGE_COLOR, HOST_STATUS_TO_BADGE_COLOR } from '../host_constants';
3027
import { FormattedDateAndTime } from '../../../../../common/components/endpoint/formatted_date_time';
3128
import { useNavigateByRouterEventHandler } from '../../../../../common/hooks/endpoint/use_navigate_by_router_event_handler';
32-
import { LinkToApp } from '../../../../../common/components/endpoint/link_to_app';
3329
import { getEndpointDetailsPath } from '../../../../common/routing';
3430
import { SecurityPageName } from '../../../../../app/types';
3531
import { useFormatUrl } from '../../../../../common/components/link_to';
36-
import { AgentDetailsReassignPolicyAction } from '../../../../../../../fleet/public';
3732
import { EndpointPolicyLink } from '../components/endpoint_policy_link';
3833
import { OutOfDate } from '../components/out_of_date';
3934

@@ -44,8 +39,6 @@ const HostIds = styled(EuiListGroupItem)`
4439
}
4540
`;
4641

47-
const openReassignFlyoutSearch = '?openReassignFlyout=true';
48-
4942
export const EndpointDetails = memo(
5043
({
5144
details,
@@ -56,19 +49,34 @@ export const EndpointDetails = memo(
5649
policyInfo?: HostInfo['policy_info'];
5750
hostStatus: HostStatus;
5851
}) => {
59-
const agentId = details.elastic.agent.id;
60-
const {
61-
url: agentDetailsUrl,
62-
appId: ingestAppId,
63-
appPath: agentDetailsAppPath,
64-
} = useAgentDetailsIngestUrl(agentId);
6552
const queryParams = useEndpointSelector(uiQueryParams);
6653
const policyStatus = useEndpointSelector(
6754
policyResponseStatus
6855
) as keyof typeof POLICY_STATUS_TO_BADGE_COLOR;
6956
const { formatUrl } = useFormatUrl(SecurityPageName.administration);
7057

71-
const detailsResultsUpper = useMemo(() => {
58+
const [policyResponseUri, policyResponseRoutePath] = useMemo(() => {
59+
// eslint-disable-next-line @typescript-eslint/naming-convention
60+
const { selected_endpoint, show, ...currentUrlParams } = queryParams;
61+
return [
62+
formatUrl(
63+
getEndpointDetailsPath({
64+
name: 'endpointPolicyResponse',
65+
...currentUrlParams,
66+
selected_endpoint: details.agent.id,
67+
})
68+
),
69+
getEndpointDetailsPath({
70+
name: 'endpointPolicyResponse',
71+
...currentUrlParams,
72+
selected_endpoint: details.agent.id,
73+
}),
74+
];
75+
}, [details.agent.id, formatUrl, queryParams]);
76+
77+
const policyStatusClickHandler = useNavigateByRouterEventHandler(policyResponseRoutePath);
78+
79+
const detailsResults = useMemo(() => {
7280
return [
7381
{
7482
title: i18n.translate('xpack.securitySolution.endpoint.details.os', {
@@ -106,55 +114,9 @@ export const EndpointDetails = memo(
106114
</EuiText>
107115
),
108116
},
109-
];
110-
}, [details, hostStatus]);
111-
112-
const [policyResponseUri, policyResponseRoutePath] = useMemo(() => {
113-
// eslint-disable-next-line @typescript-eslint/naming-convention
114-
const { selected_endpoint, show, ...currentUrlParams } = queryParams;
115-
return [
116-
formatUrl(
117-
getEndpointDetailsPath({
118-
name: 'endpointPolicyResponse',
119-
...currentUrlParams,
120-
selected_endpoint: details.agent.id,
121-
})
122-
),
123-
getEndpointDetailsPath({
124-
name: 'endpointPolicyResponse',
125-
...currentUrlParams,
126-
selected_endpoint: details.agent.id,
127-
}),
128-
];
129-
}, [details.agent.id, formatUrl, queryParams]);
130-
131-
const agentDetailsWithFlyoutPath = `${agentDetailsAppPath}${openReassignFlyoutSearch}`;
132-
const agentDetailsWithFlyoutUrl = `${agentDetailsUrl}${openReassignFlyoutSearch}`;
133-
const handleReassignEndpointsClick = useNavigateToAppEventHandler<AgentDetailsReassignPolicyAction>(
134-
ingestAppId,
135-
{
136-
path: agentDetailsWithFlyoutPath,
137-
state: {
138-
onDoneNavigateTo: [
139-
'securitySolution:administration',
140-
{
141-
path: getEndpointDetailsPath({
142-
name: 'endpointDetails',
143-
selected_endpoint: details.agent.id,
144-
}),
145-
},
146-
],
147-
},
148-
}
149-
);
150-
151-
const policyStatusClickHandler = useNavigateByRouterEventHandler(policyResponseRoutePath);
152-
153-
const detailsResultsPolicy = useMemo(() => {
154-
return [
155117
{
156118
title: i18n.translate('xpack.securitySolution.endpoint.details.policy', {
157-
defaultMessage: 'Integration Policy',
119+
defaultMessage: 'Policy',
158120
}),
159121
description: (
160122
<EuiFlexGroup alignItems="center">
@@ -198,7 +160,7 @@ export const EndpointDetails = memo(
198160
},
199161
{
200162
title: i18n.translate('xpack.securitySolution.endpoint.details.policyStatus', {
201-
defaultMessage: 'Policy Response',
163+
defaultMessage: 'Policy Status',
202164
}),
203165
description: (
204166
// https://github.com/elastic/eui/issues/4530
@@ -210,7 +172,7 @@ export const EndpointDetails = memo(
210172
onClick={policyStatusClickHandler}
211173
onClickAriaLabel={i18n.translate(
212174
'xpack.securitySolution.endpoint.details.policyStatus',
213-
{ defaultMessage: 'Policy Response' }
175+
{ defaultMessage: 'Policy Status' }
214176
)}
215177
>
216178
<EuiText size="m">
@@ -223,10 +185,12 @@ export const EndpointDetails = memo(
223185
</EuiBadge>
224186
),
225187
},
226-
];
227-
}, [details, policyResponseUri, policyStatus, policyStatusClickHandler, policyInfo]);
228-
const detailsResultsLower = useMemo(() => {
229-
return [
188+
{
189+
title: i18n.translate('xpack.securitySolution.endpoint.details.endpointVersion', {
190+
defaultMessage: 'Endpoint Version',
191+
}),
192+
description: <EuiText>{details.agent.version}</EuiText>,
193+
},
230194
{
231195
title: i18n.translate('xpack.securitySolution.endpoint.details.ipAddress', {
232196
defaultMessage: 'IP Address',
@@ -241,70 +205,23 @@ export const EndpointDetails = memo(
241205
</EuiListGroup>
242206
),
243207
},
244-
{
245-
title: i18n.translate('xpack.securitySolution.endpoint.details.hostname', {
246-
defaultMessage: 'Hostname',
247-
}),
248-
description: <EuiText className="eui-textBreakAll">{details.host.hostname}</EuiText>,
249-
},
250-
{
251-
title: i18n.translate('xpack.securitySolution.endpoint.details.endpointVersion', {
252-
defaultMessage: 'Endpoint Version',
253-
}),
254-
description: <EuiText>{details.agent.version}</EuiText>,
255-
},
256208
];
257-
}, [details.agent.version, details.host.hostname, details.host.ip]);
209+
}, [
210+
details,
211+
hostStatus,
212+
policyResponseUri,
213+
policyStatus,
214+
policyStatusClickHandler,
215+
policyInfo,
216+
]);
258217

259218
return (
260219
<>
261220
<EuiSpacer size="l" />
262221
<EuiDescriptionList
263222
type="column"
264-
listItems={detailsResultsUpper}
265-
data-test-subj="endpointDetailsUpperList"
266-
/>
267-
<EuiHorizontalRule margin="m" />
268-
<EuiDescriptionList
269-
type="column"
270-
listItems={detailsResultsPolicy}
271-
data-test-subj="endpointDetailsPolicyList"
272-
/>
273-
<EuiSpacer size="m" />
274-
<LinkToApp
275-
appId={ingestAppId}
276-
appPath={agentDetailsWithFlyoutPath}
277-
href={agentDetailsWithFlyoutUrl}
278-
onClick={handleReassignEndpointsClick}
279-
data-test-subj="endpointDetailsLinkToIngest"
280-
>
281-
<EuiFlexGroup
282-
direction="row"
283-
gutterSize="xs"
284-
justifyContent="flexStart"
285-
alignItems="center"
286-
>
287-
<EuiFlexItem grow={false}>
288-
<EuiIcon type="managementApp" className="linkToAppIcon" />
289-
</EuiFlexItem>
290-
<EuiFlexItem grow={false}>
291-
<EuiText>
292-
<FormattedMessage
293-
id="xpack.securitySolution.endpoint.details.linkToIngestTitle"
294-
defaultMessage="Reassign Policy"
295-
/>
296-
</EuiText>
297-
</EuiFlexItem>
298-
<EuiFlexItem grow={false}>
299-
<EuiIcon type="popout" className="linkToAppPopoutIcon" />
300-
</EuiFlexItem>
301-
</EuiFlexGroup>
302-
</LinkToApp>
303-
<EuiHorizontalRule margin="m" />
304-
<EuiDescriptionList
305-
type="column"
306-
listItems={detailsResultsLower}
307-
data-test-subj="endpointDetailsLowerList"
223+
listItems={detailsResults}
224+
data-test-subj="endpointDetailsList"
308225
/>
309226
</>
310227
);

x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,14 +166,14 @@ export const EndpointDetailsFlyout = memo(() => {
166166
style={{ zIndex: 4001 }}
167167
data-test-subj="endpointDetailsFlyout"
168168
size="m"
169-
paddingSize="m"
169+
paddingSize="l"
170170
>
171-
<EuiFlyoutHeader hasBorder>
171+
<EuiFlyoutHeader>
172172
{hostDetailsLoading ? (
173173
<EuiLoadingContent lines={1} />
174174
) : (
175175
<EuiToolTip content={hostDetails?.host?.hostname} anchorClassName="eui-textTruncate">
176-
<EuiTitle size="s">
176+
<EuiTitle>
177177
<h2
178178
style={{ overflow: 'hidden', textOverflow: 'ellipsis' }}
179179
data-test-subj="endpointDetailsFlyoutTitle"

x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/hooks/use_endpoint_action_items.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,30 @@ export const useEndpointActionItems = (
155155
/>
156156
),
157157
},
158+
{
159+
icon: 'gear',
160+
key: 'agentPolicyReassignLink',
161+
'data-test-subj': 'agentPolicyReassignLink',
162+
navigateAppId: 'fleet',
163+
navigateOptions: {
164+
path: `#${
165+
pagePathGetters.fleet_agent_details({
166+
agentId: fleetAgentId,
167+
})[1]
168+
}/activity?openReassignFlyout=true`,
169+
},
170+
href: `${getUrlForApp('fleet')}#${
171+
pagePathGetters.fleet_agent_details({
172+
agentId: fleetAgentId,
173+
})[1]
174+
}/activity?openReassignFlyout=true`,
175+
children: (
176+
<FormattedMessage
177+
id="xpack.securitySolution.endpoint.actions.agentPolicyReassign"
178+
defaultMessage="Reassign agent policy"
179+
/>
180+
),
181+
},
158182
];
159183
}
160184

x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,6 @@ describe('when on the endpoint list page', () => {
516516

517517
describe('when there is a selected host in the url', () => {
518518
let hostDetails: HostInfo;
519-
let elasticAgentId: string;
520519
let renderAndWaitForData: () => Promise<ReturnType<AppContextTestRender['render']>>;
521520
const mockEndpointListApi = (mockedPolicyResponse?: HostPolicyResponse) => {
522521
const {
@@ -546,8 +545,6 @@ describe('when on the endpoint list page', () => {
546545
query_strategy_version,
547546
};
548547

549-
elasticAgentId = hostDetails.metadata.elastic.agent.id;
550-
551548
const policy = docGenerator.generatePolicyPackagePolicy();
552549
policy.id = hostDetails.metadata.Endpoint.policy.applied.id;
553550

@@ -738,37 +735,11 @@ describe('when on the endpoint list page', () => {
738735
);
739736
});
740737

741-
it('should include the link to reassignment in Ingest', async () => {
742-
coreStart.application.getUrlForApp.mockReturnValue('/app/fleet');
743-
const renderResult = await renderAndWaitForData();
744-
const linkToReassign = await renderResult.findByTestId('endpointDetailsLinkToIngest');
745-
expect(linkToReassign).not.toBeNull();
746-
expect(linkToReassign.textContent).toEqual('Reassign Policy');
747-
expect(linkToReassign.getAttribute('href')).toEqual(
748-
`/app/fleet#/fleet/agents/${elasticAgentId}/activity?openReassignFlyout=true`
749-
);
750-
});
751-
752738
it('should show the Take Action button', async () => {
753739
const renderResult = await renderAndWaitForData();
754740
expect(renderResult.getByTestId('endpointDetailsActionsButton')).not.toBeNull();
755741
});
756742

757-
describe('when link to reassignment in Ingest is clicked', () => {
758-
beforeEach(async () => {
759-
coreStart.application.getUrlForApp.mockReturnValue('/app/fleet');
760-
const renderResult = await renderAndWaitForData();
761-
const linkToReassign = await renderResult.findByTestId('endpointDetailsLinkToIngest');
762-
reactTestingLibrary.act(() => {
763-
reactTestingLibrary.fireEvent.click(linkToReassign);
764-
});
765-
});
766-
767-
it('should navigate to Ingest without full page refresh', () => {
768-
expect(coreStart.application.navigateToApp.mock.calls).toHaveLength(1);
769-
});
770-
});
771-
772743
describe('when showing host Policy Response panel', () => {
773744
let renderResult: ReturnType<typeof render>;
774745
beforeEach(async () => {
@@ -1139,5 +1110,12 @@ describe('when on the endpoint list page', () => {
11391110
const agentDetailsLink = await renderResult.findByTestId('agentDetailsLink');
11401111
expect(agentDetailsLink.getAttribute('href')).toEqual(`/app/fleet#/fleet/agents/${agentId}`);
11411112
});
1113+
1114+
it('navigates to the Ingest Agent Details page with policy reassign', async () => {
1115+
const agentPolicyReassignLink = await renderResult.findByTestId('agentPolicyReassignLink');
1116+
expect(agentPolicyReassignLink.getAttribute('href')).toEqual(
1117+
`/app/fleet#/fleet/agents/${agentId}/activity?openReassignFlyout=true`
1118+
);
1119+
});
11421120
});
11431121
});

x-pack/plugins/translations/translations/ja-JP.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20131,10 +20131,8 @@
2013120131
"xpack.securitySolution.endpoint.details.endpointVersion": "エンドポイントバージョン",
2013220132
"xpack.securitySolution.endpoint.details.errorBody": "フライアウトを終了して、利用可能なホストを選択してください。",
2013320133
"xpack.securitySolution.endpoint.details.errorTitle": "ホストが見つかりませんでした",
20134-
"xpack.securitySolution.endpoint.details.hostname": "ホスト名",
2013520134
"xpack.securitySolution.endpoint.details.ipAddress": "IPアドレス",
2013620135
"xpack.securitySolution.endpoint.details.lastSeen": "前回の認識",
20137-
"xpack.securitySolution.endpoint.details.linkToIngestTitle": "ポリシーの再割り当て",
2013820136
"xpack.securitySolution.endpoint.details.noPolicyResponse": "ポリシー応答がありません",
2013920137
"xpack.securitySolution.endpoint.details.os": "OS",
2014020138
"xpack.securitySolution.endpoint.details.policy": "統合ポリシー",

0 commit comments

Comments
 (0)