mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[SIEM] Replace AutoSizer with use-resize-observer (#56588)
This commit is contained in:
parent
23306d8097
commit
ad5daba2ea
17 changed files with 1139 additions and 557 deletions
|
@ -913,6 +913,14 @@
|
|||
'@types/tslib',
|
||||
],
|
||||
},
|
||||
{
|
||||
groupSlug: 'use-resize-observer',
|
||||
groupName: 'use-resize-observer related packages',
|
||||
packageNames: [
|
||||
'use-resize-observer',
|
||||
'@types/use-resize-observer',
|
||||
],
|
||||
},
|
||||
{
|
||||
groupSlug: 'uuid',
|
||||
groupName: 'uuid related packages',
|
||||
|
|
|
@ -1,27 +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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import React from 'react';
|
||||
import { AutoSizer } from '..';
|
||||
|
||||
storiesOf('components/AutoSizer', module).add('example', () => (
|
||||
<div>
|
||||
<AutoSizer>
|
||||
{({ measureRef, content }) => (
|
||||
<div ref={measureRef} style={{ border: '1px solid tomato' }}>
|
||||
<div>
|
||||
{'width: '}
|
||||
{content.width}
|
||||
</div>
|
||||
<div>
|
||||
{'height: '}
|
||||
{content.height}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
));
|
|
@ -1,182 +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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import isEqual from 'lodash/fp/isEqual';
|
||||
import React from 'react';
|
||||
import ResizeObserver from 'resize-observer-polyfill';
|
||||
|
||||
interface Measurement {
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
interface Measurements {
|
||||
bounds: Measurement;
|
||||
content: Measurement;
|
||||
windowMeasurement: Measurement;
|
||||
}
|
||||
|
||||
interface AutoSizerProps {
|
||||
detectAnyWindowResize?: boolean;
|
||||
bounds?: boolean;
|
||||
content?: boolean;
|
||||
onResize?: (size: Measurements) => void;
|
||||
children: (
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
args: { measureRef: (instance: HTMLElement | null) => any } & Measurements
|
||||
) => React.ReactNode;
|
||||
}
|
||||
|
||||
interface AutoSizerState {
|
||||
boundsMeasurement: Measurement;
|
||||
contentMeasurement: Measurement;
|
||||
windowMeasurement: Measurement;
|
||||
}
|
||||
|
||||
/** A hard-fork of the `infra` `AutoSizer` ಠ_ಠ */
|
||||
export class AutoSizer extends React.PureComponent<AutoSizerProps, AutoSizerState> {
|
||||
public element: HTMLElement | null = null;
|
||||
public resizeObserver: ResizeObserver | null = null;
|
||||
public windowWidth: number = -1;
|
||||
|
||||
public readonly state = {
|
||||
boundsMeasurement: {
|
||||
height: void 0,
|
||||
width: void 0,
|
||||
},
|
||||
contentMeasurement: {
|
||||
height: void 0,
|
||||
width: void 0,
|
||||
},
|
||||
windowMeasurement: {
|
||||
height: void 0,
|
||||
width: void 0,
|
||||
},
|
||||
};
|
||||
|
||||
constructor(props: AutoSizerProps) {
|
||||
super(props);
|
||||
if (this.props.detectAnyWindowResize) {
|
||||
window.addEventListener('resize', this.updateMeasurement);
|
||||
}
|
||||
this.resizeObserver = new ResizeObserver(entries => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.target === this.element) {
|
||||
this.measure(entry);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
if (this.resizeObserver) {
|
||||
this.resizeObserver.disconnect();
|
||||
this.resizeObserver = null;
|
||||
}
|
||||
if (this.props.detectAnyWindowResize) {
|
||||
window.removeEventListener('resize', this.updateMeasurement);
|
||||
}
|
||||
}
|
||||
|
||||
public measure = (entry: ResizeObserverEntry | null) => {
|
||||
if (!this.element) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { content = true, bounds = false } = this.props;
|
||||
const {
|
||||
boundsMeasurement: previousBoundsMeasurement,
|
||||
contentMeasurement: previousContentMeasurement,
|
||||
windowMeasurement: previousWindowMeasurement,
|
||||
} = this.state;
|
||||
|
||||
const boundsRect = bounds ? this.element.getBoundingClientRect() : null;
|
||||
const boundsMeasurement = boundsRect
|
||||
? {
|
||||
height: this.element.getBoundingClientRect().height,
|
||||
width: this.element.getBoundingClientRect().width,
|
||||
}
|
||||
: previousBoundsMeasurement;
|
||||
const windowMeasurement: Measurement = {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
};
|
||||
|
||||
if (
|
||||
this.props.detectAnyWindowResize &&
|
||||
boundsMeasurement &&
|
||||
boundsMeasurement.width &&
|
||||
this.windowWidth !== -1 &&
|
||||
this.windowWidth > window.innerWidth
|
||||
) {
|
||||
const gap = this.windowWidth - window.innerWidth;
|
||||
boundsMeasurement.width = boundsMeasurement.width - gap;
|
||||
}
|
||||
this.windowWidth = window.innerWidth;
|
||||
const contentRect = content && entry ? entry.contentRect : null;
|
||||
const contentMeasurement =
|
||||
contentRect && entry
|
||||
? {
|
||||
height: entry.contentRect.height,
|
||||
width: entry.contentRect.width,
|
||||
}
|
||||
: previousContentMeasurement;
|
||||
|
||||
if (
|
||||
isEqual(boundsMeasurement, previousBoundsMeasurement) &&
|
||||
isEqual(contentMeasurement, previousContentMeasurement) &&
|
||||
isEqual(windowMeasurement, previousWindowMeasurement)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
if (!this.resizeObserver) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({ boundsMeasurement, contentMeasurement, windowMeasurement });
|
||||
|
||||
if (this.props.onResize) {
|
||||
this.props.onResize({
|
||||
bounds: boundsMeasurement,
|
||||
content: contentMeasurement,
|
||||
windowMeasurement,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
public render() {
|
||||
const { children } = this.props;
|
||||
const { boundsMeasurement, contentMeasurement, windowMeasurement } = this.state;
|
||||
|
||||
return children({
|
||||
bounds: boundsMeasurement,
|
||||
content: contentMeasurement,
|
||||
windowMeasurement,
|
||||
measureRef: this.storeRef,
|
||||
});
|
||||
}
|
||||
|
||||
private updateMeasurement = () => {
|
||||
window.setTimeout(() => {
|
||||
this.measure(null);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
private storeRef = (element: HTMLElement | null) => {
|
||||
if (this.element && this.resizeObserver) {
|
||||
this.resizeObserver.unobserve(this.element);
|
||||
}
|
||||
|
||||
if (element && this.resizeObserver) {
|
||||
this.resizeObserver.observe(element);
|
||||
}
|
||||
|
||||
this.element = element;
|
||||
};
|
||||
}
|
|
@ -331,7 +331,7 @@ describe('AreaChart', () => {
|
|||
});
|
||||
|
||||
it(`should render area chart`, () => {
|
||||
expect(shallowWrapper.find('AutoSizer')).toHaveLength(1);
|
||||
expect(shallowWrapper.find('WrappedByAutoSizer')).toHaveLength(1);
|
||||
expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
@ -344,7 +344,7 @@ describe('AreaChart', () => {
|
|||
});
|
||||
|
||||
it(`should render a chart place holder`, () => {
|
||||
expect(shallowWrapper.find('AutoSizer')).toHaveLength(0);
|
||||
expect(shallowWrapper.find('WrappedByAutoSizer')).toHaveLength(0);
|
||||
expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(1);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -16,7 +16,8 @@ import {
|
|||
RecursivePartial,
|
||||
} from '@elastic/charts';
|
||||
import { getOr, get, isNull, isNumber } from 'lodash/fp';
|
||||
import { AutoSizer } from '../auto_sizer';
|
||||
import useResizeObserver from 'use-resize-observer';
|
||||
|
||||
import { ChartPlaceHolder } from './chart_place_holder';
|
||||
import { useTimeZone } from '../../lib/kibana';
|
||||
import {
|
||||
|
@ -124,35 +125,24 @@ export const AreaChartBase = React.memo(AreaChartBaseComponent);
|
|||
|
||||
AreaChartBase.displayName = 'AreaChartBase';
|
||||
|
||||
export const AreaChartComponent = ({
|
||||
areaChart,
|
||||
configs,
|
||||
}: {
|
||||
interface AreaChartComponentProps {
|
||||
areaChart: ChartSeriesData[] | null | undefined;
|
||||
configs?: ChartSeriesConfigs | undefined;
|
||||
}) => {
|
||||
}
|
||||
|
||||
export const AreaChartComponent: React.FC<AreaChartComponentProps> = ({ areaChart, configs }) => {
|
||||
const { ref: measureRef, width, height } = useResizeObserver<HTMLDivElement>({});
|
||||
const customHeight = get('customHeight', configs);
|
||||
const customWidth = get('customWidth', configs);
|
||||
const chartHeight = getChartHeight(customHeight, height);
|
||||
const chartWidth = getChartWidth(customWidth, width);
|
||||
|
||||
return checkIfAnyValidSeriesExist(areaChart) ? (
|
||||
<AutoSizer detectAnyWindowResize={false} content>
|
||||
{({ measureRef, content: { height, width } }) => (
|
||||
<WrappedByAutoSizer ref={measureRef} height={getChartHeight(customHeight, height)}>
|
||||
<AreaChartBase
|
||||
data={areaChart}
|
||||
height={getChartHeight(customHeight, height)}
|
||||
width={getChartWidth(customWidth, width)}
|
||||
configs={configs}
|
||||
/>
|
||||
</WrappedByAutoSizer>
|
||||
)}
|
||||
</AutoSizer>
|
||||
<WrappedByAutoSizer ref={measureRef} height={chartHeight}>
|
||||
<AreaChartBase data={areaChart} height={chartHeight} width={chartWidth} configs={configs} />
|
||||
</WrappedByAutoSizer>
|
||||
) : (
|
||||
<ChartPlaceHolder
|
||||
height={getChartHeight(customHeight)}
|
||||
width={getChartWidth(customWidth)}
|
||||
data={areaChart}
|
||||
/>
|
||||
<ChartPlaceHolder height={chartHeight} width={chartWidth} data={areaChart} />
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -278,7 +278,7 @@ describe.each(chartDataSets)('BarChart with valid data [%o]', data => {
|
|||
});
|
||||
|
||||
it(`should render chart`, () => {
|
||||
expect(shallowWrapper.find('AutoSizer')).toHaveLength(1);
|
||||
expect(shallowWrapper.find('WrappedByAutoSizer')).toHaveLength(1);
|
||||
expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
@ -290,8 +290,8 @@ describe.each(chartHolderDataSets)('BarChart with invalid data [%o]', data => {
|
|||
shallowWrapper = shallow(<BarChartComponent configs={mockConfig} barChart={data} />);
|
||||
});
|
||||
|
||||
it(`should render chart holder`, () => {
|
||||
expect(shallowWrapper.find('AutoSizer')).toHaveLength(0);
|
||||
it(`should render a ChartPlaceHolder`, () => {
|
||||
expect(shallowWrapper.find('WrappedByAutoSizer')).toHaveLength(0);
|
||||
expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,9 +8,9 @@ import React from 'react';
|
|||
import { Chart, BarSeries, Axis, Position, ScaleType, Settings } from '@elastic/charts';
|
||||
import { getOr, get, isNumber } from 'lodash/fp';
|
||||
import deepmerge from 'deepmerge';
|
||||
import useResizeObserver from 'use-resize-observer';
|
||||
|
||||
import { useTimeZone } from '../../lib/kibana';
|
||||
import { AutoSizer } from '../auto_sizer';
|
||||
import { ChartPlaceHolder } from './chart_place_holder';
|
||||
import {
|
||||
chartDefaultSettings,
|
||||
|
@ -99,40 +99,25 @@ export const BarChartBase = React.memo(BarChartBaseComponent);
|
|||
|
||||
BarChartBase.displayName = 'BarChartBase';
|
||||
|
||||
export const BarChartComponent = ({
|
||||
barChart,
|
||||
configs,
|
||||
}: {
|
||||
interface BarChartComponentProps {
|
||||
barChart: ChartSeriesData[] | null | undefined;
|
||||
configs?: ChartSeriesConfigs | undefined;
|
||||
}) => {
|
||||
}
|
||||
|
||||
export const BarChartComponent: React.FC<BarChartComponentProps> = ({ barChart, configs }) => {
|
||||
const { ref: measureRef, width, height } = useResizeObserver<HTMLDivElement>({});
|
||||
const customHeight = get('customHeight', configs);
|
||||
const customWidth = get('customWidth', configs);
|
||||
const chartHeight = getChartHeight(customHeight, height);
|
||||
const chartWidth = getChartWidth(customWidth, width);
|
||||
|
||||
return checkIfAnyValidSeriesExist(barChart) ? (
|
||||
<AutoSizer detectAnyWindowResize={false} content>
|
||||
{({ measureRef, content: { height, width } }) => (
|
||||
<WrappedByAutoSizer ref={measureRef} height={getChartHeight(customHeight, height)}>
|
||||
<BarChartBaseComponent
|
||||
height={getChartHeight(customHeight, height)}
|
||||
width={getChartWidth(customWidth, width)}
|
||||
data={barChart}
|
||||
configs={configs}
|
||||
/>
|
||||
</WrappedByAutoSizer>
|
||||
)}
|
||||
</AutoSizer>
|
||||
<WrappedByAutoSizer ref={measureRef} height={chartHeight}>
|
||||
<BarChartBase height={chartHeight} width={chartHeight} data={barChart} configs={configs} />
|
||||
</WrappedByAutoSizer>
|
||||
) : (
|
||||
<ChartPlaceHolder
|
||||
height={getChartHeight(customHeight)}
|
||||
width={getChartWidth(customWidth)}
|
||||
data={barChart}
|
||||
/>
|
||||
<ChartPlaceHolder height={chartHeight} width={chartWidth} data={barChart} />
|
||||
);
|
||||
};
|
||||
|
||||
BarChartComponent.displayName = 'BarChartComponent';
|
||||
|
||||
export const BarChart = React.memo(BarChartComponent);
|
||||
|
||||
BarChart.displayName = 'BarChart';
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { MockedProvider } from 'react-apollo/test-utils';
|
||||
import useResizeObserver from 'use-resize-observer';
|
||||
|
||||
import { mockIndexPattern, TestProviders } from '../../mock';
|
||||
import { wait } from '../../lib/helpers';
|
||||
|
@ -27,6 +28,10 @@ mockUseFetchIndexPatterns.mockImplementation(() => [
|
|||
},
|
||||
]);
|
||||
|
||||
const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock;
|
||||
jest.mock('use-resize-observer');
|
||||
mockUseResizeObserver.mockImplementation(() => ({}));
|
||||
|
||||
const from = 1566943856794;
|
||||
const to = 1566857456791;
|
||||
|
||||
|
|
|
@ -9,13 +9,13 @@ import deepEqual from 'fast-deep-equal';
|
|||
import { getOr, isEmpty, isEqual, union } from 'lodash/fp';
|
||||
import React, { useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import useResizeObserver from 'use-resize-observer';
|
||||
|
||||
import { BrowserFields } from '../../containers/source';
|
||||
import { TimelineQuery } from '../../containers/timeline';
|
||||
import { Direction } from '../../graphql/types';
|
||||
import { useKibana } from '../../lib/kibana';
|
||||
import { KqlMode } from '../../store/timeline/model';
|
||||
import { AutoSizer } from '../auto_sizer';
|
||||
import { HeaderSection } from '../header_section';
|
||||
import { ColumnHeader } from '../timeline/body/column_headers/column_header';
|
||||
import { defaultHeaders } from '../timeline/body/column_headers/default_headers';
|
||||
|
@ -95,6 +95,7 @@ const EventsViewerComponent: React.FC<Props> = ({
|
|||
toggleColumn,
|
||||
utilityBar,
|
||||
}) => {
|
||||
const { ref: measureRef, width = 0 } = useResizeObserver<HTMLDivElement>({});
|
||||
const columnsHeader = isEmpty(columns) ? defaultHeaders : columns;
|
||||
const kibana = useKibana();
|
||||
const combinedQueries = combineQueries({
|
||||
|
@ -120,115 +121,108 @@ const EventsViewerComponent: React.FC<Props> = ({
|
|||
|
||||
return (
|
||||
<StyledEuiPanel data-test-subj="events-viewer-panel">
|
||||
<AutoSizer detectAnyWindowResize={true} content>
|
||||
{({ measureRef, content: { width = 0 } }) => (
|
||||
<>
|
||||
<WrappedByAutoSizer ref={measureRef}>
|
||||
<div
|
||||
data-test-subj="events-viewer-measured"
|
||||
style={{ height: '0px', width: '100%' }}
|
||||
/>
|
||||
</WrappedByAutoSizer>
|
||||
<>
|
||||
<WrappedByAutoSizer ref={measureRef}>
|
||||
<div data-test-subj="events-viewer-measured" style={{ height: '0px', width: '100%' }} />
|
||||
</WrappedByAutoSizer>
|
||||
|
||||
{combinedQueries != null ? (
|
||||
<TimelineQuery
|
||||
fields={queryFields}
|
||||
filterQuery={combinedQueries.filterQuery}
|
||||
id={id}
|
||||
indexPattern={indexPattern}
|
||||
limit={itemsPerPage}
|
||||
sortField={{
|
||||
sortFieldId: sort.columnId,
|
||||
direction: sort.sortDirection as Direction,
|
||||
}}
|
||||
sourceId="default"
|
||||
>
|
||||
{({
|
||||
events,
|
||||
getUpdatedAt,
|
||||
inspect,
|
||||
loading,
|
||||
loadMore,
|
||||
pageInfo,
|
||||
refetch,
|
||||
totalCount = 0,
|
||||
}) => {
|
||||
const totalCountMinusDeleted =
|
||||
totalCount > 0 ? totalCount - deletedEventIds.length : 0;
|
||||
{combinedQueries != null ? (
|
||||
<TimelineQuery
|
||||
fields={queryFields}
|
||||
filterQuery={combinedQueries.filterQuery}
|
||||
id={id}
|
||||
indexPattern={indexPattern}
|
||||
limit={itemsPerPage}
|
||||
sortField={{
|
||||
sortFieldId: sort.columnId,
|
||||
direction: sort.sortDirection as Direction,
|
||||
}}
|
||||
sourceId="default"
|
||||
>
|
||||
{({
|
||||
events,
|
||||
getUpdatedAt,
|
||||
inspect,
|
||||
loading,
|
||||
loadMore,
|
||||
pageInfo,
|
||||
refetch,
|
||||
totalCount = 0,
|
||||
}) => {
|
||||
const totalCountMinusDeleted =
|
||||
totalCount > 0 ? totalCount - deletedEventIds.length : 0;
|
||||
|
||||
const subtitle = `${
|
||||
i18n.SHOWING
|
||||
}: ${totalCountMinusDeleted.toLocaleString()} ${timelineTypeContext.unit?.(
|
||||
totalCountMinusDeleted
|
||||
) ?? i18n.UNIT(totalCountMinusDeleted)}`;
|
||||
const subtitle = `${
|
||||
i18n.SHOWING
|
||||
}: ${totalCountMinusDeleted.toLocaleString()} ${timelineTypeContext.unit?.(
|
||||
totalCountMinusDeleted
|
||||
) ?? i18n.UNIT(totalCountMinusDeleted)}`;
|
||||
|
||||
// TODO: Reset eventDeletedIds/eventLoadingIds on refresh/loadmore (getUpdatedAt)
|
||||
return (
|
||||
<>
|
||||
<HeaderSection
|
||||
// TODO: Reset eventDeletedIds/eventLoadingIds on refresh/loadmore (getUpdatedAt)
|
||||
return (
|
||||
<>
|
||||
<HeaderSection
|
||||
id={id}
|
||||
subtitle={utilityBar ? undefined : subtitle}
|
||||
title={timelineTypeContext?.title ?? i18n.EVENTS}
|
||||
>
|
||||
{headerFilterGroup}
|
||||
</HeaderSection>
|
||||
|
||||
{utilityBar?.(refetch, totalCountMinusDeleted)}
|
||||
|
||||
<div
|
||||
data-test-subj={`events-container-loading-${loading}`}
|
||||
style={{ width: `${width}px` }}
|
||||
>
|
||||
<ManageTimelineContext
|
||||
loading={loading}
|
||||
width={width}
|
||||
type={timelineTypeContext}
|
||||
>
|
||||
<TimelineRefetch
|
||||
id={id}
|
||||
subtitle={utilityBar ? undefined : subtitle}
|
||||
title={timelineTypeContext?.title ?? i18n.EVENTS}
|
||||
>
|
||||
{headerFilterGroup}
|
||||
</HeaderSection>
|
||||
inputId="global"
|
||||
inspect={inspect}
|
||||
loading={loading}
|
||||
refetch={refetch}
|
||||
/>
|
||||
|
||||
{utilityBar?.(refetch, totalCountMinusDeleted)}
|
||||
<StatefulBody
|
||||
browserFields={browserFields}
|
||||
data={events.filter(e => !deletedEventIds.includes(e._id))}
|
||||
id={id}
|
||||
isEventViewer={true}
|
||||
height={height}
|
||||
sort={sort}
|
||||
toggleColumn={toggleColumn}
|
||||
/>
|
||||
|
||||
<div
|
||||
data-test-subj={`events-container-loading-${loading}`}
|
||||
style={{ width: `${width}px` }}
|
||||
>
|
||||
<ManageTimelineContext
|
||||
loading={loading}
|
||||
width={width}
|
||||
type={timelineTypeContext}
|
||||
>
|
||||
<TimelineRefetch
|
||||
id={id}
|
||||
inputId="global"
|
||||
inspect={inspect}
|
||||
loading={loading}
|
||||
refetch={refetch}
|
||||
/>
|
||||
|
||||
<StatefulBody
|
||||
browserFields={browserFields}
|
||||
data={events.filter(e => !deletedEventIds.includes(e._id))}
|
||||
id={id}
|
||||
isEventViewer={true}
|
||||
height={height}
|
||||
sort={sort}
|
||||
toggleColumn={toggleColumn}
|
||||
/>
|
||||
|
||||
<Footer
|
||||
compact={isCompactFooter(width)}
|
||||
getUpdatedAt={getUpdatedAt}
|
||||
hasNextPage={getOr(false, 'hasNextPage', pageInfo)!}
|
||||
height={footerHeight}
|
||||
isEventViewer={true}
|
||||
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)}
|
||||
/>
|
||||
</ManageTimelineContext>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</TimelineQuery>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</AutoSizer>
|
||||
<Footer
|
||||
compact={isCompactFooter(width)}
|
||||
getUpdatedAt={getUpdatedAt}
|
||||
hasNextPage={getOr(false, 'hasNextPage', pageInfo)!}
|
||||
height={footerHeight}
|
||||
isEventViewer={true}
|
||||
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)}
|
||||
/>
|
||||
</ManageTimelineContext>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</TimelineQuery>
|
||||
) : null}
|
||||
</>
|
||||
</StyledEuiPanel>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { MockedProvider } from 'react-apollo/test-utils';
|
||||
import useResizeObserver from 'use-resize-observer';
|
||||
|
||||
import { wait } from '../../lib/helpers';
|
||||
import { mockIndexPattern, TestProviders } from '../../mock';
|
||||
|
@ -26,6 +27,10 @@ mockUseFetchIndexPatterns.mockImplementation(() => [
|
|||
},
|
||||
]);
|
||||
|
||||
const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock;
|
||||
jest.mock('use-resize-observer');
|
||||
mockUseResizeObserver.mockImplementation(() => ({}));
|
||||
|
||||
const from = 1566943856794;
|
||||
const to = 1566857456791;
|
||||
|
||||
|
|
|
@ -1,10 +1,793 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Timeline rendering renders correctly against snapshot 1`] = `
|
||||
<AutoSizer
|
||||
content={true}
|
||||
detectAnyWindowResize={true}
|
||||
<TimelineContainer
|
||||
data-test-subj="timeline"
|
||||
direction="column"
|
||||
gutterSize="none"
|
||||
justifyContent="flexStart"
|
||||
>
|
||||
<Component />
|
||||
</AutoSizer>
|
||||
<WrappedByAutoSizer>
|
||||
<TimelineHeader
|
||||
browserFields={
|
||||
Object {
|
||||
"agent": Object {
|
||||
"fields": Object {
|
||||
"agent.ephemeral_id": Object {
|
||||
"aggregatable": true,
|
||||
"category": "agent",
|
||||
"description": "Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but \`agent.id\` does not.",
|
||||
"example": "8a4f500f",
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "agent.ephemeral_id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"agent.hostname": Object {
|
||||
"aggregatable": true,
|
||||
"category": "agent",
|
||||
"description": null,
|
||||
"example": null,
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "agent.hostname",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"agent.id": Object {
|
||||
"aggregatable": true,
|
||||
"category": "agent",
|
||||
"description": "Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.",
|
||||
"example": "8a4f500d",
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "agent.id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"agent.name": Object {
|
||||
"aggregatable": true,
|
||||
"category": "agent",
|
||||
"description": "Name of the agent. This is a name that can be given to an agent. This can be helpful if for example two Filebeat instances are running on the same host but a human readable separation is needed on which Filebeat instance data is coming from. If no name is given, the name is often left empty.",
|
||||
"example": "foo",
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "agent.name",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
"auditd": Object {
|
||||
"fields": Object {
|
||||
"auditd.data.a0": Object {
|
||||
"aggregatable": true,
|
||||
"category": "auditd",
|
||||
"description": null,
|
||||
"example": null,
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
],
|
||||
"name": "auditd.data.a0",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"auditd.data.a1": Object {
|
||||
"aggregatable": true,
|
||||
"category": "auditd",
|
||||
"description": null,
|
||||
"example": null,
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
],
|
||||
"name": "auditd.data.a1",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"auditd.data.a2": Object {
|
||||
"aggregatable": true,
|
||||
"category": "auditd",
|
||||
"description": null,
|
||||
"example": null,
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
],
|
||||
"name": "auditd.data.a2",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
"base": Object {
|
||||
"fields": Object {
|
||||
"@timestamp": Object {
|
||||
"aggregatable": true,
|
||||
"category": "base",
|
||||
"description": "Date/time when the event originated. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events.",
|
||||
"example": "2016-05-23T08:05:34.853Z",
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "@timestamp",
|
||||
"searchable": true,
|
||||
"type": "date",
|
||||
},
|
||||
},
|
||||
},
|
||||
"client": Object {
|
||||
"fields": Object {
|
||||
"client.address": Object {
|
||||
"aggregatable": true,
|
||||
"category": "client",
|
||||
"description": "Some event client addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the \`.address\` field. Then it should be duplicated to \`.ip\` or \`.domain\`, depending on which one it is.",
|
||||
"example": null,
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "client.address",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"client.bytes": Object {
|
||||
"aggregatable": true,
|
||||
"category": "client",
|
||||
"description": "Bytes sent from the client to the server.",
|
||||
"example": "184",
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "client.bytes",
|
||||
"searchable": true,
|
||||
"type": "number",
|
||||
},
|
||||
"client.domain": Object {
|
||||
"aggregatable": true,
|
||||
"category": "client",
|
||||
"description": "Client domain.",
|
||||
"example": null,
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "client.domain",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"client.geo.country_iso_code": Object {
|
||||
"aggregatable": true,
|
||||
"category": "client",
|
||||
"description": "Country ISO code.",
|
||||
"example": "CA",
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "client.geo.country_iso_code",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
"cloud": Object {
|
||||
"fields": Object {
|
||||
"cloud.account.id": Object {
|
||||
"aggregatable": true,
|
||||
"category": "cloud",
|
||||
"description": "The cloud account or organization id used to identify different entities in a multi-tenant environment. Examples: AWS account id, Google Cloud ORG Id, or other unique identifier.",
|
||||
"example": "666777888999",
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "cloud.account.id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"cloud.availability_zone": Object {
|
||||
"aggregatable": true,
|
||||
"category": "cloud",
|
||||
"description": "Availability zone in which this host is running.",
|
||||
"example": "us-east-1c",
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "cloud.availability_zone",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
"container": Object {
|
||||
"fields": Object {
|
||||
"container.id": Object {
|
||||
"aggregatable": true,
|
||||
"category": "container",
|
||||
"description": "Unique container id.",
|
||||
"example": null,
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "container.id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"container.image.name": Object {
|
||||
"aggregatable": true,
|
||||
"category": "container",
|
||||
"description": "Name of the image the container was built on.",
|
||||
"example": null,
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "container.image.name",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"container.image.tag": Object {
|
||||
"aggregatable": true,
|
||||
"category": "container",
|
||||
"description": "Container image tag.",
|
||||
"example": null,
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "container.image.tag",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
"destination": Object {
|
||||
"fields": Object {
|
||||
"destination.address": Object {
|
||||
"aggregatable": true,
|
||||
"category": "destination",
|
||||
"description": "Some event destination addresses are defined ambiguously. The event will sometimes list an IP, a domain or a unix socket. You should always store the raw address in the \`.address\` field. Then it should be duplicated to \`.ip\` or \`.domain\`, depending on which one it is.",
|
||||
"example": null,
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "destination.address",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"destination.bytes": Object {
|
||||
"aggregatable": true,
|
||||
"category": "destination",
|
||||
"description": "Bytes sent from the destination to the source.",
|
||||
"example": "184",
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "destination.bytes",
|
||||
"searchable": true,
|
||||
"type": "number",
|
||||
},
|
||||
"destination.domain": Object {
|
||||
"aggregatable": true,
|
||||
"category": "destination",
|
||||
"description": "Destination domain.",
|
||||
"example": null,
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "destination.domain",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
"destination.ip": Object {
|
||||
"aggregatable": true,
|
||||
"category": "destination",
|
||||
"description": "IP address of the destination. Can be one or multiple IPv4 or IPv6 addresses.",
|
||||
"example": "",
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "destination.ip",
|
||||
"searchable": true,
|
||||
"type": "ip",
|
||||
},
|
||||
"destination.port": Object {
|
||||
"aggregatable": true,
|
||||
"category": "destination",
|
||||
"description": "Port of the destination.",
|
||||
"example": "",
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "destination.port",
|
||||
"searchable": true,
|
||||
"type": "long",
|
||||
},
|
||||
},
|
||||
},
|
||||
"event": Object {
|
||||
"fields": Object {
|
||||
"event.end": Object {
|
||||
"aggregatable": true,
|
||||
"category": "event",
|
||||
"description": "event.end contains the date when the event ended or when the activity was last observed.",
|
||||
"example": null,
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"apm-*-transaction*",
|
||||
"auditbeat-*",
|
||||
"endgame-*",
|
||||
"filebeat-*",
|
||||
"packetbeat-*",
|
||||
"winlogbeat-*",
|
||||
],
|
||||
"name": "event.end",
|
||||
"searchable": true,
|
||||
"type": "date",
|
||||
},
|
||||
},
|
||||
},
|
||||
"source": Object {
|
||||
"fields": Object {
|
||||
"source.ip": Object {
|
||||
"aggregatable": true,
|
||||
"category": "source",
|
||||
"description": "IP address of the source. Can be one or multiple IPv4 or IPv6 addresses.",
|
||||
"example": "",
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "source.ip",
|
||||
"searchable": true,
|
||||
"type": "ip",
|
||||
},
|
||||
"source.port": Object {
|
||||
"aggregatable": true,
|
||||
"category": "source",
|
||||
"description": "Port of the source.",
|
||||
"example": "",
|
||||
"format": "",
|
||||
"indexes": Array [
|
||||
"auditbeat",
|
||||
"filebeat",
|
||||
"packetbeat",
|
||||
],
|
||||
"name": "source.port",
|
||||
"searchable": true,
|
||||
"type": "long",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
dataProviders={
|
||||
Array [
|
||||
Object {
|
||||
"and": Array [],
|
||||
"enabled": true,
|
||||
"excluded": false,
|
||||
"id": "id-Provider 1",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 1",
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"operator": ":",
|
||||
"value": "Provider 1",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"and": Array [],
|
||||
"enabled": true,
|
||||
"excluded": false,
|
||||
"id": "id-Provider 2",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 2",
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"operator": ":",
|
||||
"value": "Provider 2",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"and": Array [],
|
||||
"enabled": true,
|
||||
"excluded": false,
|
||||
"id": "id-Provider 3",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 3",
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"operator": ":",
|
||||
"value": "Provider 3",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"and": Array [],
|
||||
"enabled": true,
|
||||
"excluded": false,
|
||||
"id": "id-Provider 4",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 4",
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"operator": ":",
|
||||
"value": "Provider 4",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"and": Array [],
|
||||
"enabled": true,
|
||||
"excluded": false,
|
||||
"id": "id-Provider 5",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 5",
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"operator": ":",
|
||||
"value": "Provider 5",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"and": Array [],
|
||||
"enabled": true,
|
||||
"excluded": false,
|
||||
"id": "id-Provider 6",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 6",
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"operator": ":",
|
||||
"value": "Provider 6",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"and": Array [],
|
||||
"enabled": true,
|
||||
"excluded": false,
|
||||
"id": "id-Provider 7",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 7",
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"operator": ":",
|
||||
"value": "Provider 7",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"and": Array [],
|
||||
"enabled": true,
|
||||
"excluded": false,
|
||||
"id": "id-Provider 8",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 8",
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"operator": ":",
|
||||
"value": "Provider 8",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"and": Array [],
|
||||
"enabled": true,
|
||||
"excluded": false,
|
||||
"id": "id-Provider 9",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 9",
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"operator": ":",
|
||||
"value": "Provider 9",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"and": Array [],
|
||||
"enabled": true,
|
||||
"excluded": false,
|
||||
"id": "id-Provider 10",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 10",
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"operator": ":",
|
||||
"value": "Provider 10",
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
id="foo"
|
||||
indexPattern={
|
||||
Object {
|
||||
"fields": Array [
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "@timestamp",
|
||||
"searchable": true,
|
||||
"type": "date",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "@version",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.ephemeral_id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.hostname",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test1",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test2",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test3",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test4",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test5",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test6",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test7",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test8",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "host.name",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
],
|
||||
"title": "filebeat-*,auditbeat-*,packetbeat-*",
|
||||
}
|
||||
}
|
||||
onChangeDataProviderKqlQuery={[MockFunction]}
|
||||
onChangeDroppableAndProvider={[MockFunction]}
|
||||
onDataProviderEdited={[MockFunction]}
|
||||
onDataProviderRemoved={[MockFunction]}
|
||||
onToggleDataProviderEnabled={[MockFunction]}
|
||||
onToggleDataProviderExcluded={[MockFunction]}
|
||||
show={true}
|
||||
showCallOutUnauthorizedMsg={false}
|
||||
sort={
|
||||
Object {
|
||||
"columnId": "@timestamp",
|
||||
"sortDirection": "desc",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</WrappedByAutoSizer>
|
||||
<Connect(Component)
|
||||
id="foo"
|
||||
indexPattern={
|
||||
Object {
|
||||
"fields": Array [
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "@timestamp",
|
||||
"searchable": true,
|
||||
"type": "date",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "@version",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.ephemeral_id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.hostname",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test1",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test2",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test3",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test4",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test5",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test6",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test7",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test8",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "host.name",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
],
|
||||
"title": "filebeat-*,auditbeat-*,packetbeat-*",
|
||||
}
|
||||
}
|
||||
inputId="timeline"
|
||||
/>
|
||||
<Connect(Component)
|
||||
eventType="raw"
|
||||
fields={
|
||||
Array [
|
||||
"@timestamp",
|
||||
"event.severity",
|
||||
"event.category",
|
||||
"event.action",
|
||||
"host.name",
|
||||
"source.ip",
|
||||
"destination.ip",
|
||||
"destination.bytes",
|
||||
"user.name",
|
||||
"_id",
|
||||
"message",
|
||||
]
|
||||
}
|
||||
filterQuery="{\\"bool\\":{\\"must\\":[],\\"filter\\":[{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 1\\"}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 2\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 3\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 4\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 5\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 6\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 7\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 8\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 9\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"match_phrase\\":{\\"name\\":\\"Provider 10\\"}}],\\"minimum_should_match\\":1}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"filter\\":[{\\"bool\\":{\\"should\\":[{\\"range\\":{\\"@timestamp\\":{\\"gte\\":1521830963132}}}],\\"minimum_should_match\\":1}},{\\"bool\\":{\\"should\\":[{\\"range\\":{\\"@timestamp\\":{\\"lte\\":1521862432253}}}],\\"minimum_should_match\\":1}}]}}]}}],\\"should\\":[],\\"must_not\\":[]}}"
|
||||
id="foo"
|
||||
indexToAdd={Array []}
|
||||
limit={5}
|
||||
sortField={
|
||||
Object {
|
||||
"direction": "desc",
|
||||
"sortFieldId": "@timestamp",
|
||||
}
|
||||
}
|
||||
sourceId="default"
|
||||
>
|
||||
<Component />
|
||||
</Connect(Component)>
|
||||
</TimelineContainer>
|
||||
`;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { MockedProvider } from 'react-apollo/test-utils';
|
||||
import useResizeObserver from 'use-resize-observer';
|
||||
|
||||
import { timelineQuery } from '../../containers/timeline/index.gql_query';
|
||||
import { mockBrowserFields } from '../../containers/source/mock';
|
||||
|
@ -29,6 +30,10 @@ const testFlyoutHeight = 980;
|
|||
|
||||
jest.mock('../../lib/kibana');
|
||||
|
||||
const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock;
|
||||
jest.mock('use-resize-observer');
|
||||
mockUseResizeObserver.mockImplementation(() => ({}));
|
||||
|
||||
describe('Timeline', () => {
|
||||
const sort: Sort = {
|
||||
columnId: '@timestamp',
|
||||
|
|
|
@ -8,13 +8,13 @@ import { EuiFlexGroup } from '@elastic/eui';
|
|||
import { getOr, isEmpty } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import useResizeObserver from 'use-resize-observer';
|
||||
|
||||
import { BrowserFields } from '../../containers/source';
|
||||
import { TimelineQuery } from '../../containers/timeline';
|
||||
import { Direction } from '../../graphql/types';
|
||||
import { useKibana } from '../../lib/kibana';
|
||||
import { KqlMode, EventType } from '../../store/timeline/model';
|
||||
import { AutoSizer } from '../auto_sizer';
|
||||
import { ColumnHeader } from './body/column_headers/column_header';
|
||||
import { defaultHeaders } from './body/column_headers/default_headers';
|
||||
import { Sort } from './body/sort';
|
||||
|
@ -88,7 +88,7 @@ interface Props {
|
|||
}
|
||||
|
||||
/** The parent Timeline component */
|
||||
export const TimelineComponent = ({
|
||||
export const TimelineComponent: React.FC<Props> = ({
|
||||
browserFields,
|
||||
columns,
|
||||
dataProviders,
|
||||
|
@ -118,7 +118,10 @@ export const TimelineComponent = ({
|
|||
start,
|
||||
sort,
|
||||
toggleColumn,
|
||||
}: Props) => {
|
||||
}) => {
|
||||
const { ref: measureRef, width = 0, height: timelineHeaderHeight = 0 } = useResizeObserver<
|
||||
HTMLDivElement
|
||||
>({});
|
||||
const kibana = useKibana();
|
||||
const combinedQueries = combineQueries({
|
||||
config: esQuery.getEsQueryConfig(kibana.services.uiSettings),
|
||||
|
@ -132,101 +135,98 @@ export const TimelineComponent = ({
|
|||
end,
|
||||
});
|
||||
const columnsHeader = isEmpty(columns) ? defaultHeaders : columns;
|
||||
|
||||
return (
|
||||
<AutoSizer detectAnyWindowResize={true} content>
|
||||
{({ measureRef, content: { height: timelineHeaderHeight = 0, width = 0 } }) => (
|
||||
<TimelineContainer
|
||||
data-test-subj="timeline"
|
||||
direction="column"
|
||||
gutterSize="none"
|
||||
justifyContent="flexStart"
|
||||
<TimelineContainer
|
||||
data-test-subj="timeline"
|
||||
direction="column"
|
||||
gutterSize="none"
|
||||
justifyContent="flexStart"
|
||||
>
|
||||
<WrappedByAutoSizer ref={measureRef as React.RefObject<HTMLDivElement>}>
|
||||
<TimelineHeader
|
||||
browserFields={browserFields}
|
||||
id={id}
|
||||
indexPattern={indexPattern}
|
||||
dataProviders={dataProviders}
|
||||
onChangeDataProviderKqlQuery={onChangeDataProviderKqlQuery}
|
||||
onChangeDroppableAndProvider={onChangeDroppableAndProvider}
|
||||
onDataProviderEdited={onDataProviderEdited}
|
||||
onDataProviderRemoved={onDataProviderRemoved}
|
||||
onToggleDataProviderEnabled={onToggleDataProviderEnabled}
|
||||
onToggleDataProviderExcluded={onToggleDataProviderExcluded}
|
||||
show={show}
|
||||
showCallOutUnauthorizedMsg={showCallOutUnauthorizedMsg}
|
||||
sort={sort}
|
||||
/>
|
||||
</WrappedByAutoSizer>
|
||||
<TimelineKqlFetch id={id} indexPattern={indexPattern} inputId="timeline" />
|
||||
{combinedQueries != null ? (
|
||||
<TimelineQuery
|
||||
eventType={eventType}
|
||||
id={id}
|
||||
indexToAdd={indexToAdd}
|
||||
fields={columnsHeader.map(c => c.id)}
|
||||
sourceId="default"
|
||||
limit={itemsPerPage}
|
||||
filterQuery={combinedQueries.filterQuery}
|
||||
sortField={{
|
||||
sortFieldId: sort.columnId,
|
||||
direction: sort.sortDirection as Direction,
|
||||
}}
|
||||
>
|
||||
<WrappedByAutoSizer ref={measureRef}>
|
||||
<TimelineHeader
|
||||
browserFields={browserFields}
|
||||
id={id}
|
||||
indexPattern={indexPattern}
|
||||
dataProviders={dataProviders}
|
||||
onChangeDataProviderKqlQuery={onChangeDataProviderKqlQuery}
|
||||
onChangeDroppableAndProvider={onChangeDroppableAndProvider}
|
||||
onDataProviderEdited={onDataProviderEdited}
|
||||
onDataProviderRemoved={onDataProviderRemoved}
|
||||
onToggleDataProviderEnabled={onToggleDataProviderEnabled}
|
||||
onToggleDataProviderExcluded={onToggleDataProviderExcluded}
|
||||
show={show}
|
||||
showCallOutUnauthorizedMsg={showCallOutUnauthorizedMsg}
|
||||
sort={sort}
|
||||
/>
|
||||
</WrappedByAutoSizer>
|
||||
<TimelineKqlFetch id={id} indexPattern={indexPattern} inputId="timeline" />
|
||||
{combinedQueries != null ? (
|
||||
<TimelineQuery
|
||||
eventType={eventType}
|
||||
id={id}
|
||||
indexToAdd={indexToAdd}
|
||||
fields={columnsHeader.map(c => c.id)}
|
||||
sourceId="default"
|
||||
limit={itemsPerPage}
|
||||
filterQuery={combinedQueries.filterQuery}
|
||||
sortField={{
|
||||
sortFieldId: sort.columnId,
|
||||
direction: sort.sortDirection as Direction,
|
||||
}}
|
||||
>
|
||||
{({
|
||||
events,
|
||||
inspect,
|
||||
loading,
|
||||
totalCount,
|
||||
pageInfo,
|
||||
loadMore,
|
||||
getUpdatedAt,
|
||||
refetch,
|
||||
}) => (
|
||||
<ManageTimelineContext loading={loading || loadingIndexName} width={width}>
|
||||
<TimelineRefetch
|
||||
id={id}
|
||||
inputId="timeline"
|
||||
inspect={inspect}
|
||||
loading={loading}
|
||||
refetch={refetch}
|
||||
/>
|
||||
<StatefulBody
|
||||
browserFields={browserFields}
|
||||
data={events}
|
||||
id={id}
|
||||
height={calculateBodyHeight({
|
||||
flyoutHeight,
|
||||
flyoutHeaderHeight,
|
||||
timelineHeaderHeight,
|
||||
timelineFooterHeight: footerHeight,
|
||||
})}
|
||||
sort={sort}
|
||||
toggleColumn={toggleColumn}
|
||||
/>
|
||||
<Footer
|
||||
serverSideEventCount={totalCount}
|
||||
hasNextPage={getOr(false, 'hasNextPage', pageInfo)!}
|
||||
height={footerHeight}
|
||||
isLive={isLive}
|
||||
isLoading={loading || loadingIndexName}
|
||||
itemsCount={events.length}
|
||||
itemsPerPage={itemsPerPage}
|
||||
itemsPerPageOptions={itemsPerPageOptions}
|
||||
onChangeItemsPerPage={onChangeItemsPerPage}
|
||||
onLoadMore={loadMore}
|
||||
nextCursor={getOr(null, 'endCursor.value', pageInfo)!}
|
||||
tieBreaker={getOr(null, 'endCursor.tiebreaker', pageInfo)}
|
||||
getUpdatedAt={getUpdatedAt}
|
||||
compact={isCompactFooter(width)}
|
||||
/>
|
||||
</ManageTimelineContext>
|
||||
)}
|
||||
</TimelineQuery>
|
||||
) : null}
|
||||
</TimelineContainer>
|
||||
)}
|
||||
</AutoSizer>
|
||||
{({
|
||||
events,
|
||||
inspect,
|
||||
loading,
|
||||
totalCount,
|
||||
pageInfo,
|
||||
loadMore,
|
||||
getUpdatedAt,
|
||||
refetch,
|
||||
}) => (
|
||||
<ManageTimelineContext loading={loading || loadingIndexName} width={width}>
|
||||
<TimelineRefetch
|
||||
id={id}
|
||||
inputId="timeline"
|
||||
inspect={inspect}
|
||||
loading={loading}
|
||||
refetch={refetch}
|
||||
/>
|
||||
<StatefulBody
|
||||
browserFields={browserFields}
|
||||
data={events}
|
||||
id={id}
|
||||
height={calculateBodyHeight({
|
||||
flyoutHeight,
|
||||
flyoutHeaderHeight,
|
||||
timelineHeaderHeight,
|
||||
timelineFooterHeight: footerHeight,
|
||||
})}
|
||||
sort={sort}
|
||||
toggleColumn={toggleColumn}
|
||||
/>
|
||||
<Footer
|
||||
serverSideEventCount={totalCount}
|
||||
hasNextPage={getOr(false, 'hasNextPage', pageInfo)!}
|
||||
height={footerHeight}
|
||||
isLive={isLive}
|
||||
isLoading={loading || loadingIndexName}
|
||||
itemsCount={events.length}
|
||||
itemsPerPage={itemsPerPage}
|
||||
itemsPerPageOptions={itemsPerPageOptions}
|
||||
onChangeItemsPerPage={onChangeItemsPerPage}
|
||||
onLoadMore={loadMore}
|
||||
nextCursor={getOr(null, 'endCursor.value', pageInfo)!}
|
||||
tieBreaker={getOr(null, 'endCursor.tiebreaker', pageInfo)}
|
||||
getUpdatedAt={getUpdatedAt}
|
||||
compact={isCompactFooter(width)}
|
||||
/>
|
||||
</ManageTimelineContext>
|
||||
)}
|
||||
</TimelineQuery>
|
||||
) : null}
|
||||
</TimelineContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
import React from 'react';
|
||||
import { Redirect, Route, Switch } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
import useResizeObserver from 'use-resize-observer';
|
||||
|
||||
import { AutoSizer } from '../../components/auto_sizer';
|
||||
import { DragDropContextWrapper } from '../../components/drag_and_drop/drag_drop_context_wrapper';
|
||||
import { Flyout, flyoutHeaderHeight } from '../../components/flyout';
|
||||
import { HeaderGlobal } from '../../components/header_global';
|
||||
|
@ -61,96 +61,91 @@ const calculateFlyoutHeight = ({
|
|||
windowHeight: number;
|
||||
}): number => Math.max(0, windowHeight - globalHeaderSize);
|
||||
|
||||
export const HomePage: React.FC = () => (
|
||||
<AutoSizer detectAnyWindowResize={true} content>
|
||||
{({ measureRef, windowMeasurement: { height: windowHeight = 0 } }) => (
|
||||
<WrappedByAutoSizer data-test-subj="wrapped-by-auto-sizer" ref={measureRef}>
|
||||
<HeaderGlobal />
|
||||
export const HomePage: React.FC = () => {
|
||||
const { ref: measureRef, height: windowHeight = 0 } = useResizeObserver<HTMLDivElement>({});
|
||||
const flyoutHeight = calculateFlyoutHeight({
|
||||
globalHeaderSize: globalHeaderHeightPx,
|
||||
windowHeight,
|
||||
});
|
||||
|
||||
<Main data-test-subj="pageContainer">
|
||||
<WithSource sourceId="default">
|
||||
{({ browserFields, indexPattern, indicesExist }) => (
|
||||
<DragDropContextWrapper browserFields={browserFields}>
|
||||
<UseUrlState indexPattern={indexPattern} navTabs={navTabs} />
|
||||
{indicesExistOrDataTemporarilyUnavailable(indicesExist) && (
|
||||
<>
|
||||
<AutoSaveWarningMsg />
|
||||
<Flyout
|
||||
flyoutHeight={calculateFlyoutHeight({
|
||||
globalHeaderSize: globalHeaderHeightPx,
|
||||
windowHeight,
|
||||
})}
|
||||
headerHeight={flyoutHeaderHeight}
|
||||
timelineId="timeline-1"
|
||||
usersViewing={usersViewing}
|
||||
>
|
||||
<StatefulTimeline
|
||||
flyoutHeaderHeight={flyoutHeaderHeight}
|
||||
flyoutHeight={calculateFlyoutHeight({
|
||||
globalHeaderSize: globalHeaderHeightPx,
|
||||
windowHeight,
|
||||
})}
|
||||
id="timeline-1"
|
||||
/>
|
||||
</Flyout>
|
||||
</>
|
||||
)}
|
||||
return (
|
||||
<WrappedByAutoSizer data-test-subj="wrapped-by-auto-sizer" ref={measureRef}>
|
||||
<HeaderGlobal />
|
||||
|
||||
<Switch>
|
||||
<Redirect exact from="/" to={`/${SiemPageName.overview}`} />
|
||||
<Route
|
||||
path={`/:pageName(${SiemPageName.overview})`}
|
||||
render={() => <Overview />}
|
||||
/>
|
||||
<Route
|
||||
path={`/:pageName(${SiemPageName.hosts})`}
|
||||
render={({ match }) => <HostsContainer url={match.url} />}
|
||||
/>
|
||||
<Route
|
||||
path={`/:pageName(${SiemPageName.network})`}
|
||||
render={({ location, match }) => (
|
||||
<NetworkContainer location={location} url={match.url} />
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={`/:pageName(${SiemPageName.detections})`}
|
||||
render={({ location, match }) => (
|
||||
<DetectionEngineContainer location={location} url={match.url} />
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={`/:pageName(${SiemPageName.timelines})`}
|
||||
render={() => <Timelines />}
|
||||
/>
|
||||
<Route path="/link-to" render={props => <LinkToPage {...props} />} />
|
||||
<Route
|
||||
path="/ml-hosts"
|
||||
render={({ location, match }) => (
|
||||
<MlHostConditionalContainer location={location} url={match.url} />
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path="/ml-network"
|
||||
render={({ location, match }) => (
|
||||
<MlNetworkConditionalContainer location={location} url={match.url} />
|
||||
)}
|
||||
/>
|
||||
<Route path={`/:pageName(${SiemPageName.case})`}>
|
||||
<Case />
|
||||
</Route>
|
||||
<Route render={() => <NotFoundPage />} />
|
||||
</Switch>
|
||||
</DragDropContextWrapper>
|
||||
)}
|
||||
</WithSource>
|
||||
</Main>
|
||||
<Main data-test-subj="pageContainer">
|
||||
<WithSource sourceId="default">
|
||||
{({ browserFields, indexPattern, indicesExist }) => (
|
||||
<DragDropContextWrapper browserFields={browserFields}>
|
||||
<UseUrlState indexPattern={indexPattern} navTabs={navTabs} />
|
||||
{indicesExistOrDataTemporarilyUnavailable(indicesExist) && (
|
||||
<>
|
||||
<AutoSaveWarningMsg />
|
||||
<Flyout
|
||||
flyoutHeight={flyoutHeight}
|
||||
headerHeight={flyoutHeaderHeight}
|
||||
timelineId="timeline-1"
|
||||
usersViewing={usersViewing}
|
||||
>
|
||||
<StatefulTimeline
|
||||
flyoutHeaderHeight={flyoutHeaderHeight}
|
||||
flyoutHeight={flyoutHeight}
|
||||
id="timeline-1"
|
||||
/>
|
||||
</Flyout>
|
||||
</>
|
||||
)}
|
||||
|
||||
<HelpMenu />
|
||||
<Switch>
|
||||
<Redirect exact from="/" to={`/${SiemPageName.overview}`} />
|
||||
<Route path={`/:pageName(${SiemPageName.overview})`} render={() => <Overview />} />
|
||||
<Route
|
||||
path={`/:pageName(${SiemPageName.hosts})`}
|
||||
render={({ match }) => <HostsContainer url={match.url} />}
|
||||
/>
|
||||
<Route
|
||||
path={`/:pageName(${SiemPageName.network})`}
|
||||
render={({ location, match }) => (
|
||||
<NetworkContainer location={location} url={match.url} />
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={`/:pageName(${SiemPageName.detections})`}
|
||||
render={({ location, match }) => (
|
||||
<DetectionEngineContainer location={location} url={match.url} />
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={`/:pageName(${SiemPageName.timelines})`}
|
||||
render={() => <Timelines />}
|
||||
/>
|
||||
<Route path="/link-to" render={props => <LinkToPage {...props} />} />
|
||||
<Route
|
||||
path="/ml-hosts"
|
||||
render={({ location, match }) => (
|
||||
<MlHostConditionalContainer location={location} url={match.url} />
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path="/ml-network"
|
||||
render={({ location, match }) => (
|
||||
<MlNetworkConditionalContainer location={location} url={match.url} />
|
||||
)}
|
||||
/>
|
||||
<Route path={`/:pageName(${SiemPageName.case})`}>
|
||||
<Case />
|
||||
</Route>
|
||||
<Route render={() => <NotFoundPage />} />
|
||||
</Switch>
|
||||
</DragDropContextWrapper>
|
||||
)}
|
||||
</WithSource>
|
||||
</Main>
|
||||
|
||||
<SpyRoute />
|
||||
</WrappedByAutoSizer>
|
||||
)}
|
||||
</AutoSizer>
|
||||
);
|
||||
<HelpMenu />
|
||||
|
||||
<SpyRoute />
|
||||
</WrappedByAutoSizer>
|
||||
);
|
||||
};
|
||||
|
||||
HomePage.displayName = 'HomePage';
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import React from 'react';
|
||||
import { IIndexPattern } from 'src/plugins/data/public';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import useResizeObserver from 'use-resize-observer';
|
||||
|
||||
import { mockIndexPattern } from '../../../mock/index_pattern';
|
||||
import { TestProviders } from '../../../mock/test_providers';
|
||||
|
@ -35,6 +36,10 @@ jest.mock('../../../components/query_bar', () => ({
|
|||
QueryBar: () => null,
|
||||
}));
|
||||
|
||||
const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock;
|
||||
jest.mock('use-resize-observer');
|
||||
mockUseResizeObserver.mockImplementation(() => ({}));
|
||||
|
||||
describe('body', () => {
|
||||
const scenariosMap = {
|
||||
authentications: 'AuthenticationsQueryTabBody',
|
||||
|
|
|
@ -102,6 +102,7 @@
|
|||
"@types/supertest": "^2.0.5",
|
||||
"@types/tar-fs": "^1.16.1",
|
||||
"@types/tinycolor2": "^1.4.1",
|
||||
"@types/use-resize-observer": "^6.0.0",
|
||||
"@types/uuid": "^3.4.4",
|
||||
"@types/xml-crypto": "^1.4.0",
|
||||
"@types/xml2js": "^0.4.5",
|
||||
|
@ -344,6 +345,7 @@
|
|||
"typescript-fsa-reducers": "^1.2.1",
|
||||
"ui-select": "0.19.8",
|
||||
"unstated": "^2.1.1",
|
||||
"use-resize-observer": "^6.0.0",
|
||||
"uuid": "3.3.2",
|
||||
"venn.js": "0.2.20",
|
||||
"vscode-languageserver": "^5.2.1",
|
||||
|
|
14
yarn.lock
14
yarn.lock
|
@ -5337,6 +5337,13 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e"
|
||||
integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==
|
||||
|
||||
"@types/use-resize-observer@^6.0.0":
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/use-resize-observer/-/use-resize-observer-6.0.0.tgz#d1b162b2733b22225908a7877ca7115c67f3752e"
|
||||
integrity sha512-8RD06szR+wzHpfCBFbcTEQ0OCoogoSWCyyUmWyqc5qGG2fa1sOUdwNte5dwoJWG/sh5sBU0QVZ1+9zcQGLUSVg==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/uuid@^3.4.4":
|
||||
version "3.4.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.4.tgz#7af69360fa65ef0decb41fd150bf4ca5c0cefdf5"
|
||||
|
@ -30379,6 +30386,13 @@ use-memo-one@^1.1.1:
|
|||
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.1.tgz#39e6f08fe27e422a7d7b234b5f9056af313bd22c"
|
||||
integrity sha512-oFfsyun+bP7RX8X2AskHNTxu+R3QdE/RC5IefMbqptmACAA/gfol1KDD5KRzPsGMa62sWxGZw+Ui43u6x4ddoQ==
|
||||
|
||||
use-resize-observer@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/use-resize-observer/-/use-resize-observer-6.0.0.tgz#8450ade735c386e8d93cdf983c26d34a9f478a54"
|
||||
integrity sha512-Zfe1qsZVhzfgECs+L/ZcSukyVPFGOmWtC7xZI5Lpn4PR2hNgVvD1NmQ/GYBoCSV2pJskxflJWcDzpTAt84nhvw==
|
||||
dependencies:
|
||||
resize-observer-polyfill "^1.5.1"
|
||||
|
||||
use@^2.0.0:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/use/-/use-2.0.2.tgz#ae28a0d72f93bf22422a18a2e379993112dec8e8"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue