Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
add tabs for endpoint details
  • Loading branch information
ashokaditya committed May 11, 2021
1 parent 2d05d9f commit c26a7d4
Show file tree
Hide file tree
Showing 6 changed files with 303 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* 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, { useCallback, useMemo, useState } from 'react';
import styled from 'styled-components';
import { EuiTabbedContent, EuiTabbedContentTab } from '@elastic/eui';

export enum EndpointDetailsTabsTypes {
overview = 'overview',
activityLog = 'activity-log',
}

export type EndpointDetailsTabsId =
| EndpointDetailsTabsTypes.overview
| EndpointDetailsTabsTypes.activityLog;

interface EndpointDetailsTabs {
id: string;
name: string;
content: JSX.Element;
}

const StyledEuiTabbedContent = styled(EuiTabbedContent)`
display: flex;
flex: 1;
flex-direction: column;
overflow: hidden;
> [role='tabpanel'] {
padding: 12px 0;
display: flex;
flex: 1;
flex-direction: column;
overflow: hidden;
overflow-y: auto;
::-webkit-scrollbar {
-webkit-appearance: none;
width: 7px;
}
::-webkit-scrollbar-thumb {
border-radius: 4px;
background-color: rgba(0, 0, 0, 0.5);
-webkit-box-shadow: 0 0 1px rgba(255, 255, 255, 0.5);
}
}
`;

export const EndpointDetailsFlyoutTabs = ({ tabs }: { tabs: EndpointDetailsTabs[] }) => {
const [selectedTabId, setSelectedTabId] = useState<EndpointDetailsTabsId>(
EndpointDetailsTabsTypes.overview
);

const handleTabClick = useCallback(
(tab: EuiTabbedContentTab) => setSelectedTabId(tab.id as EndpointDetailsTabsId),
[setSelectedTabId]
);

const selectedTab = useMemo(() => tabs.find((tab) => tab.id === selectedTabId), [
tabs,
selectedTabId,
]);

return (
<StyledEuiTabbedContent
data-test-subj="endpointDetailsTabs"
tabs={tabs}
selectedTab={selectedTab}
onTabClick={handleTabClick}
key="endpoint-details-tabs"
/>
);
};

EndpointDetailsFlyoutTabs.displayName = 'EndpointDetailsFlyoutTabs';
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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 styled from 'styled-components';

import { EuiComment, EuiPanel, EuiSpacer, EuiText } from '@elastic/eui';
import { EndpointAction } from '../../../../../../../common/endpoint/types';

const TimelineItem = styled(EuiComment)`
figure {
border: 0;
}
figcaption.euiCommentEvent__header {
background-color: transparent;
border: 0;
}
`;

const CommentItem = styled.div`
max-width: 85%;
> div {
display: inline-flex;
}
`;

export const TimelineEntry = ({ endpointAction }: { endpointAction: EndpointAction }) => {
const isIsolated = endpointAction?.data.command === 'isolate';
const timelineIcon = isIsolated ? 'lock' : 'lockOpen';
const event = `${isIsolated ? 'isolated' : 'unisolated'} the endpoint`;
const hasComment = !!endpointAction.data.comment;
return (
<TimelineItem
type="regular"
username={endpointAction.user_id}
event={event}
timelineIcon={timelineIcon}
data-test-subj="timelineEntry"
>
<EuiText size="s">{endpointAction['@timestamp']}</EuiText>
<EuiSpacer size="m" />
{hasComment ? (
<CommentItem>
<EuiPanel hasBorder={true} paddingSize="s">
<EuiText size="s">
<p>{endpointAction.data.comment}</p>
</EuiText>
</EuiPanel>
</CommentItem>
) : undefined}
</TimelineItem>
);
};

TimelineEntry.displayName = 'TimelineEntry';
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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 { EuiFieldSearch, EuiSpacer } from '@elastic/eui';
import { TimelineEntry } from './components/timeline_entry';
import { EndpointAction } from '../../../../../../common/endpoint/types';

export const EndpointActivityLog = ({ endpointActions }: { endpointActions: EndpointAction[] }) => (
<>
<EuiFieldSearch compressed fullWidth placeholder="Search activity log" />
<EuiSpacer size="l" />
{endpointActions.map((endpointAction) => (
<TimelineEntry key={endpointAction['@timestamp']} endpointAction={endpointAction} />
))}
</>
);

EndpointActivityLog.displayName = 'EndpointActivityLog';
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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, { ComponentType } from 'react';

import { EndpointDetailsFlyoutTabs } from './components/endpoint_details_tabs';
import { EndpointActivityLog } from './endpoint_activity_log';
import { EndpointDetailsFlyout } from '.';
import { EuiThemeProvider } from '../../../../../../../../../src/plugins/kibana_react/common';

import { dummyEndpointActions } from './';

export default {
title: 'Endpoints/Endpoint Details',
component: EndpointDetailsFlyout,
decorators: [
(Story: ComponentType) => (
<EuiThemeProvider>
<Story />
</EuiThemeProvider>
),
],
};

export const Tabs = () => (
<EndpointDetailsFlyoutTabs
tabs={[
{
id: 'overview',
name: 'Overview',
content: <>{'Endpoint Details'}</>,
},
{
id: 'activity-log',
name: 'Activity Log',
content: ActivityLog(),
},
]}
/>
);

export const ActivityLog = () => <EndpointActivityLog endpointActions={dummyEndpointActions} />;
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
*/

import React, { useCallback, useEffect, memo, useMemo } from 'react';
import moment from 'moment';

import {
EuiFlyout,
EuiFlyoutBody,
Expand All @@ -16,6 +18,8 @@ import {
EuiSpacer,
EuiEmptyPrompt,
EuiToolTip,
EuiFlexGroup,
EuiFlexItem,
} from '@elastic/eui';
import { useHistory } from 'react-router-dom';
import { FormattedMessage } from '@kbn/i18n/react';
Expand All @@ -41,15 +45,65 @@ import {
policyResponseAppliedRevision,
} from '../../store/selectors';
import { EndpointDetails } from './endpoint_details';
import { EndpointActivityLog } from './endpoint_activity_log';
import { PolicyResponse } from './policy_response';
import { HostMetadata } from '../../../../../../common/endpoint/types';
import * as i18 from '../translations';
import { EndpointAction, HostMetadata } from '../../../../../../common/endpoint/types';
import { FlyoutSubHeader, FlyoutSubHeaderProps } from './components/flyout_sub_header';
import {
EndpointDetailsFlyoutTabs,
EndpointDetailsTabsTypes,
} from './components/endpoint_details_tabs';
import { useNavigateByRouterEventHandler } from '../../../../../common/hooks/endpoint/use_navigate_by_router_event_handler';
import { getEndpointListPath } from '../../../../common/routing';
import { SecurityPageName } from '../../../../../app/types';
import { useFormatUrl } from '../../../../../common/components/link_to';
import { PreferenceFormattedDateFromPrimitive } from '../../../../../common/components/formatted_date';

export const dummyEndpointActions: EndpointAction[] = [
{
action_id: '1',
'@timestamp': moment().subtract(2, 'hours').fromNow().toString(),
expiration: moment().add(1, 'day').fromNow().toString(),
type: 'INPUT_ACTION',
input_type: 'endpoint',
agents: ['x', 'y', 'z'],
user_id: 'ash',
data: {
command: 'isolate',
comment: 'Sem et tortor consequat id porta nibh venenatis cras sed.',
},
},
{
action_id: '2',
'@timestamp': moment().subtract(4, 'hours').fromNow().toString(),
expiration: moment().add(1, 'day').fromNow().toString(),
type: 'INPUT_ACTION',
input_type: 'endpoint',
agents: ['x', 'y', 'z'],
user_id: 'someone',
data: {
command: 'unisolate',
comment: 'Turpis egestas pretium aenean pharetra.',
},
},
{
action_id: '3',
'@timestamp': moment().subtract(1, 'day').fromNow().toString(),
expiration: moment().add(3, 'day').fromNow().toString(),
type: 'INPUT_ACTION',
input_type: 'endpoint',
agents: ['x', 'y', 'z'],
user_id: 'ash',
data: {
command: 'isolate',
comment:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, \
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
},
},
];

export const EndpointDetailsFlyout = memo(() => {
const history = useHistory();
const toasts = useToasts();
Expand Down Expand Up @@ -88,6 +142,7 @@ export const EndpointDetailsFlyout = memo(() => {
style={{ zIndex: 4001 }}
data-test-subj="endpointDetailsFlyout"
size="m"
paddingSize="m"
>
<EuiFlyoutHeader hasBorder>
{loading ? (
Expand Down Expand Up @@ -116,11 +171,30 @@ export const EndpointDetailsFlyout = memo(() => {
{show === 'details' && (
<>
<EuiFlyoutBody data-test-subj="endpointDetailsFlyoutBody">
<EndpointDetails
details={details}
policyInfo={policyInfo}
hostStatus={hostStatus}
/>
<EuiFlexGroup>
<EuiFlexItem>
<EndpointDetailsFlyoutTabs
tabs={[
{
id: EndpointDetailsTabsTypes.overview,
name: i18.OVERVIEW,
content: (
<EndpointDetails
details={details}
policyInfo={policyInfo}
hostStatus={hostStatus}
/>
),
},
{
id: EndpointDetailsTabsTypes.activityLog,
name: i18.ACTIVITY_LOG,
content: <EndpointActivityLog endpointActions={dummyEndpointActions} />,
},
]}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlyoutBody>
</>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* 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 { i18n } from '@kbn/i18n';

export const OVERVIEW = i18n.translate('xpack.securitySolution.endpointDetails.overview', {
defaultMessage: 'Overview',
});

export const ACTIVITY_LOG = i18n.translate('xpack.securitySolution.endpointDetails.activityLog', {
defaultMessage: 'Activity Log',
});

0 comments on commit c26a7d4

Please sign in to comment.