mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Security Solution] Hide timeline footer when Resolver is open (#71516)
* Hide the Timeline footer, in the event viewer, if Resolver is showing
This commit is contained in:
parent
fdc999769d
commit
97afee5b06
8 changed files with 123 additions and 48 deletions
|
@ -67,6 +67,8 @@ interface Props {
|
|||
sort: Sort;
|
||||
toggleColumn: (column: ColumnHeaderOptions) => void;
|
||||
utilityBar?: (refetch: inputsModel.Refetch, totalCount: number) => React.ReactNode;
|
||||
// If truthy, the graph viewer (Resolver) is showing
|
||||
graphEventId: string | undefined;
|
||||
}
|
||||
|
||||
const EventsViewerComponent: React.FC<Props> = ({
|
||||
|
@ -90,6 +92,7 @@ const EventsViewerComponent: React.FC<Props> = ({
|
|||
sort,
|
||||
toggleColumn,
|
||||
utilityBar,
|
||||
graphEventId,
|
||||
}) => {
|
||||
const columnsHeader = isEmpty(columns) ? defaultHeaders : columns;
|
||||
const kibana = useKibana();
|
||||
|
@ -191,22 +194,28 @@ const EventsViewerComponent: React.FC<Props> = ({
|
|||
toggleColumn={toggleColumn}
|
||||
/>
|
||||
|
||||
<Footer
|
||||
getUpdatedAt={getUpdatedAt}
|
||||
hasNextPage={getOr(false, 'hasNextPage', pageInfo)!}
|
||||
height={footerHeight}
|
||||
id={id}
|
||||
isLive={isLive}
|
||||
isLoading={loading}
|
||||
itemsCount={events.length}
|
||||
itemsPerPage={itemsPerPage}
|
||||
itemsPerPageOptions={itemsPerPageOptions}
|
||||
onChangeItemsPerPage={onChangeItemsPerPage}
|
||||
onLoadMore={loadMore}
|
||||
nextCursor={getOr(null, 'endCursor.value', pageInfo)!}
|
||||
serverSideEventCount={totalCountMinusDeleted}
|
||||
tieBreaker={getOr(null, 'endCursor.tiebreaker', pageInfo)}
|
||||
/>
|
||||
{
|
||||
/** Hide the footer if Resolver is showing. */
|
||||
!graphEventId && (
|
||||
<Footer
|
||||
data-test-subj="events-viewer-footer"
|
||||
getUpdatedAt={getUpdatedAt}
|
||||
hasNextPage={getOr(false, 'hasNextPage', pageInfo)!}
|
||||
height={footerHeight}
|
||||
id={id}
|
||||
isLive={isLive}
|
||||
isLoading={loading}
|
||||
itemsCount={events.length}
|
||||
itemsPerPage={itemsPerPage}
|
||||
itemsPerPageOptions={itemsPerPageOptions}
|
||||
onChangeItemsPerPage={onChangeItemsPerPage}
|
||||
onLoadMore={loadMore}
|
||||
nextCursor={getOr(null, 'endCursor.value', pageInfo)!}
|
||||
serverSideEventCount={totalCountMinusDeleted}
|
||||
tieBreaker={getOr(null, 'endCursor.tiebreaker', pageInfo)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</EventsContainerLoading>
|
||||
</>
|
||||
);
|
||||
|
@ -237,5 +246,6 @@ export const EventsViewer = React.memo(
|
|||
deepEqual(prevProps.query, nextProps.query) &&
|
||||
prevProps.start === nextProps.start &&
|
||||
prevProps.sort === nextProps.sort &&
|
||||
prevProps.utilityBar === nextProps.utilityBar
|
||||
prevProps.utilityBar === nextProps.utilityBar &&
|
||||
prevProps.graphEventId === nextProps.graphEventId
|
||||
);
|
||||
|
|
|
@ -62,6 +62,8 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
|
|||
updateItemsPerPage,
|
||||
upsertColumn,
|
||||
utilityBar,
|
||||
// If truthy, the graph viewer (Resolver) is showing
|
||||
graphEventId,
|
||||
}) => {
|
||||
const [{ browserFields, indexPatterns }] = useFetchIndexPatterns(
|
||||
defaultIndices ?? useUiSetting<string[]>(DEFAULT_INDEX_KEY)
|
||||
|
@ -135,6 +137,7 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
|
|||
sort={sort}
|
||||
toggleColumn={toggleColumn}
|
||||
utilityBar={utilityBar}
|
||||
graphEventId={graphEventId}
|
||||
/>
|
||||
</InspectButtonContainer>
|
||||
);
|
||||
|
@ -145,6 +148,7 @@ const makeMapStateToProps = () => {
|
|||
const getGlobalQuerySelector = inputsSelectors.globalQuerySelector();
|
||||
const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector();
|
||||
const getEvents = timelineSelectors.getEventsByIdSelector();
|
||||
const getTimeline = timelineSelectors.getTimelineByIdSelector();
|
||||
const mapStateToProps = (state: State, { id, defaultModel }: OwnProps) => {
|
||||
const input: inputsModel.InputsRange = getInputsTimeline(state);
|
||||
const events: TimelineModel = getEvents(state, id) ?? defaultModel;
|
||||
|
@ -174,6 +178,9 @@ const makeMapStateToProps = () => {
|
|||
query: getGlobalQuerySelector(state),
|
||||
sort,
|
||||
showCheckboxes,
|
||||
// Used to determine whether the footer should show (since it is hidden if the graph is showing.)
|
||||
// `getTimeline` actually returns `TimelineModel | undefined`
|
||||
graphEventId: (getTimeline(state, id) as TimelineModel | undefined)?.graphEventId,
|
||||
};
|
||||
};
|
||||
return mapStateToProps;
|
||||
|
@ -213,6 +220,7 @@ export const StatefulEventsViewer = connector(
|
|||
deepEqual(prevProps.pageFilters, nextProps.pageFilters) &&
|
||||
prevProps.showCheckboxes === nextProps.showCheckboxes &&
|
||||
prevProps.start === nextProps.start &&
|
||||
prevProps.utilityBar === nextProps.utilityBar
|
||||
prevProps.utilityBar === nextProps.utilityBar &&
|
||||
prevProps.graphEventId === nextProps.graphEventId
|
||||
)
|
||||
);
|
||||
|
|
|
@ -103,9 +103,6 @@ export const getEventType = (event: Ecs): Omit<EventType, 'all'> => {
|
|||
return 'raw';
|
||||
};
|
||||
|
||||
export const showGraphView = (graphEventId?: string) =>
|
||||
graphEventId != null && graphEventId.length > 0;
|
||||
|
||||
export const isInvestigateInResolverActionEnabled = (ecsData?: Ecs) => {
|
||||
return (
|
||||
get(['agent', 'type', 0], ecsData) === 'endpoint' &&
|
||||
|
|
|
@ -17,7 +17,8 @@ import { columnRenderers, rowRenderers } from './renderers';
|
|||
import { Sort } from './sort';
|
||||
import { wait } from '../../../../common/lib/helpers';
|
||||
import { useMountAppended } from '../../../../common/utils/use_mount_appended';
|
||||
import { SELECTOR_TIMELINE_BODY_CLASS_NAME } from '../styles';
|
||||
import { SELECTOR_TIMELINE_BODY_CLASS_NAME, TimelineBody } from '../styles';
|
||||
import { ReactWrapper } from '@elastic/eui/node_modules/@types/enzyme';
|
||||
|
||||
const testBodyHeight = 700;
|
||||
const mockGetNotesByIds = (eventId: string[]) => [];
|
||||
|
@ -33,8 +34,12 @@ jest.mock('react-redux', () => {
|
|||
useSelector: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../../common/components/link_to');
|
||||
|
||||
// Prevent Resolver from rendering
|
||||
jest.mock('../../graph_overlay');
|
||||
|
||||
jest.mock(
|
||||
'react-visibility-sensor',
|
||||
() => ({ children }: { children: (args: { isVisible: boolean }) => React.ReactNode }) =>
|
||||
|
@ -148,6 +153,29 @@ describe('Body', () => {
|
|||
.exists()
|
||||
).toEqual(true);
|
||||
});
|
||||
describe('when there is a graphEventId', () => {
|
||||
beforeEach(() => {
|
||||
props.graphEventId = 'graphEventId'; // any string w/ length > 0 works
|
||||
});
|
||||
it('should not render the timeline body', () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<Body {...props} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
// The value returned if `wrapper.find` returns a `TimelineBody` instance.
|
||||
type TimelineBodyEnzymeWrapper = ReactWrapper<React.ComponentProps<typeof TimelineBody>>;
|
||||
|
||||
// The first TimelineBody component
|
||||
const timelineBody: TimelineBodyEnzymeWrapper = wrapper
|
||||
.find('[data-test-subj="timeline-body"]')
|
||||
.first() as TimelineBodyEnzymeWrapper;
|
||||
|
||||
// the timeline body still renders, but it gets a `display: none` style via `styled-components`.
|
||||
expect(timelineBody.props().visible).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('action on event', () => {
|
||||
|
|
|
@ -26,7 +26,6 @@ import { EventsTable, TimelineBody, TimelineBodyGlobalStyle } from '../styles';
|
|||
import { ColumnHeaders } from './column_headers';
|
||||
import { getActionsColumnWidth } from './column_headers/helpers';
|
||||
import { Events } from './events';
|
||||
import { showGraphView } from './helpers';
|
||||
import { ColumnRenderer } from './renderers/column_renderer';
|
||||
import { RowRenderer } from './renderers/row_renderer';
|
||||
import { Sort } from './sort';
|
||||
|
@ -146,7 +145,7 @@ export const Body = React.memo<BodyProps>(
|
|||
|
||||
return (
|
||||
<>
|
||||
{showGraphView(graphEventId) && (
|
||||
{graphEventId && (
|
||||
<GraphOverlay bodyHeight={height} graphEventId={graphEventId} timelineId={id} />
|
||||
)}
|
||||
<TimelineBody
|
||||
|
@ -154,7 +153,7 @@ export const Body = React.memo<BodyProps>(
|
|||
data-timeline-id={id}
|
||||
bodyHeight={height}
|
||||
ref={containerElementRef}
|
||||
visible={show && !showGraphView(graphEventId)}
|
||||
visible={show && !graphEventId}
|
||||
>
|
||||
<EventsTable data-test-subj="events-table" columnWidths={columnWidths}>
|
||||
<ColumnHeaders
|
||||
|
|
|
@ -9,7 +9,6 @@ import React from 'react';
|
|||
import { FilterManager, IIndexPattern } from 'src/plugins/data/public';
|
||||
import deepEqual from 'fast-deep-equal';
|
||||
|
||||
import { showGraphView } from '../body/helpers';
|
||||
import { DataProviders } from '../data_providers';
|
||||
import { DataProvider } from '../data_providers/data_provider';
|
||||
import {
|
||||
|
@ -80,7 +79,7 @@ const TimelineHeaderComponent: React.FC<Props> = ({
|
|||
size="s"
|
||||
/>
|
||||
)}
|
||||
{show && !showGraphView(graphEventId) && (
|
||||
{show && !graphEventId && (
|
||||
<>
|
||||
<DataProviders
|
||||
browserFields={browserFields}
|
||||
|
|
|
@ -180,6 +180,34 @@ describe('Timeline', () => {
|
|||
'All events'
|
||||
);
|
||||
});
|
||||
|
||||
it('it shows the timeline footer', () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<MockedProvider mocks={mocks}>
|
||||
<TimelineComponent {...props} />
|
||||
</MockedProvider>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="timeline-footer"]').exists()).toEqual(true);
|
||||
});
|
||||
describe('when there is a graphEventId', () => {
|
||||
beforeEach(() => {
|
||||
props.graphEventId = 'graphEventId'; // any string w/ length > 0 works
|
||||
});
|
||||
it('should not show the timeline footer', () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<MockedProvider mocks={mocks}>
|
||||
<TimelineComponent {...props} />
|
||||
</MockedProvider>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="timeline-footer"]').exists()).toEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('event wire up', () => {
|
||||
|
|
|
@ -282,27 +282,33 @@ export const TimelineComponent: React.FC<Props> = ({
|
|||
toggleColumn={toggleColumn}
|
||||
/>
|
||||
</StyledEuiFlyoutBody>
|
||||
<StyledEuiFlyoutFooter
|
||||
data-test-subj="eui-flyout-footer"
|
||||
className="timeline-flyout-footer"
|
||||
>
|
||||
<Footer
|
||||
getUpdatedAt={getUpdatedAt}
|
||||
hasNextPage={getOr(false, 'hasNextPage', pageInfo)!}
|
||||
height={footerHeight}
|
||||
id={id}
|
||||
isLive={isLive}
|
||||
isLoading={loading || loadingIndexName}
|
||||
itemsCount={events.length}
|
||||
itemsPerPage={itemsPerPage}
|
||||
itemsPerPageOptions={itemsPerPageOptions}
|
||||
nextCursor={getOr(null, 'endCursor.value', pageInfo)!}
|
||||
onChangeItemsPerPage={onChangeItemsPerPage}
|
||||
onLoadMore={loadMore}
|
||||
serverSideEventCount={totalCount}
|
||||
tieBreaker={getOr(null, 'endCursor.tiebreaker', pageInfo)}
|
||||
/>
|
||||
</StyledEuiFlyoutFooter>
|
||||
{
|
||||
/** Hide the footer if Resolver is showing. */
|
||||
!graphEventId && (
|
||||
<StyledEuiFlyoutFooter
|
||||
data-test-subj="eui-flyout-footer"
|
||||
className="timeline-flyout-footer"
|
||||
>
|
||||
<Footer
|
||||
data-test-subj="timeline-footer"
|
||||
getUpdatedAt={getUpdatedAt}
|
||||
hasNextPage={getOr(false, 'hasNextPage', pageInfo)!}
|
||||
height={footerHeight}
|
||||
id={id}
|
||||
isLive={isLive}
|
||||
isLoading={loading || loadingIndexName}
|
||||
itemsCount={events.length}
|
||||
itemsPerPage={itemsPerPage}
|
||||
itemsPerPageOptions={itemsPerPageOptions}
|
||||
nextCursor={getOr(null, 'endCursor.value', pageInfo)!}
|
||||
onChangeItemsPerPage={onChangeItemsPerPage}
|
||||
onLoadMore={loadMore}
|
||||
serverSideEventCount={totalCount}
|
||||
tieBreaker={getOr(null, 'endCursor.tiebreaker', pageInfo)}
|
||||
/>
|
||||
</StyledEuiFlyoutFooter>
|
||||
)
|
||||
}
|
||||
</>
|
||||
);
|
||||
}}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue