Skip to content

Commit

Permalink
[SIEM] Add events histogram (elastic#45403) (elastic#46536)
Browse files Browse the repository at this point in the history
* add events histogram

* move away from auto date histogram aggregation

* fallback to auto-date-histogram if interval is not available

* styling for histogram

* add add narrowDownTimerange event

* isolate matrixOverTimeHistogram component

* split events series

* add lable for missing group

* fix styled component syntax + change prop name

* add unit test

* fix props passed to styled component

* add integration test

* pass filterQuery prop to events tab on Host details page

* reduce bucket number for bar chart

* update types

* display barchart only when data is available

* change function style

* fix types

* add unit test

* add spacer

* pass updateDateRange down to host details components

* update snapshot
  • Loading branch information
angorayc authored Sep 25, 2019
1 parent 4ec27fb commit c878857
Show file tree
Hide file tree
Showing 34 changed files with 2,027 additions and 406 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@
import { ShallowWrapper, shallow } from 'enzyme';
import * as React from 'react';

import { AreaChartBaseComponent, AreaChartWithCustomPrompt } from './areachart';
import { ChartConfigsData, ChartHolder } from './common';
import { AreaChartBaseComponent, AreaChartWithCustomPrompt, AreaChart } from './areachart';
import { ChartHolder, ChartSeriesData } from './common';
import { ScaleType, AreaSeries, Axis } from '@elastic/charts';

jest.mock('@elastic/charts');

const customHeight = '100px';
const customWidth = '120px';
describe('AreaChartBaseComponent', () => {
let shallowWrapper: ShallowWrapper;
const mockAreaChartData: ChartConfigsData[] = [
const mockAreaChartData: ChartSeriesData[] = [
{
key: 'uniqueSourceIpsHistogram',
value: [
Expand All @@ -39,7 +40,11 @@ describe('AreaChartBaseComponent', () => {
describe('render', () => {
beforeAll(() => {
shallowWrapper = shallow(
<AreaChartBaseComponent height={100} width={120} data={mockAreaChartData} />
<AreaChartBaseComponent
height={customHeight}
width={customWidth}
data={mockAreaChartData}
/>
);
});

Expand All @@ -65,8 +70,8 @@ describe('AreaChartBaseComponent', () => {
beforeAll(() => {
shallowWrapper = shallow(
<AreaChartBaseComponent
height={100}
width={120}
height={customHeight}
width={customWidth}
data={mockAreaChartData}
configs={configs}
/>
Expand Down Expand Up @@ -118,7 +123,11 @@ describe('AreaChartBaseComponent', () => {
describe('render with default configs if no customized configs given', () => {
beforeAll(() => {
shallowWrapper = shallow(
<AreaChartBaseComponent height={100} width={120} data={mockAreaChartData} />
<AreaChartBaseComponent
height={customHeight}
width={customWidth}
data={mockAreaChartData}
/>
);
});

Expand Down Expand Up @@ -220,9 +229,11 @@ describe('AreaChartWithCustomPrompt', () => {
],
],
],
])('renders areachart', (data: ChartConfigsData[] | [] | null | undefined) => {
])('renders areachart', (data: ChartSeriesData[] | [] | null | undefined) => {
beforeAll(() => {
shallowWrapper = shallow(<AreaChartWithCustomPrompt height={100} width={120} data={data} />);
shallowWrapper = shallow(
<AreaChartWithCustomPrompt height={customHeight} width={customWidth} data={data} />
);
});

it('render AreaChartBaseComponent', () => {
Expand Down Expand Up @@ -310,9 +321,11 @@ describe('AreaChartWithCustomPrompt', () => {
},
],
],
])('renders prompt', (data: ChartConfigsData[] | [] | null | undefined) => {
])('renders prompt', (data: ChartSeriesData[] | [] | null | undefined) => {
beforeAll(() => {
shallowWrapper = shallow(<AreaChartWithCustomPrompt height={100} width={120} data={data} />);
shallowWrapper = shallow(
<AreaChartWithCustomPrompt height={customHeight} width={customWidth} data={data} />
);
});

it('render Chart Holder', () => {
Expand All @@ -321,3 +334,36 @@ describe('AreaChartWithCustomPrompt', () => {
});
});
});

describe('AreaChart', () => {
let shallowWrapper: ShallowWrapper;
const mockConfig = {
series: {
xScaleType: ScaleType.Time,
yScaleType: ScaleType.Linear,
stackAccessors: ['g'],
},
axis: {
xTickFormatter: jest.fn(),
yTickFormatter: jest.fn(),
tickSize: 8,
},
customHeight: 324,
};

it('should render if data exist', () => {
const mockData = [
{ key: 'uniqueSourceIps', value: [{ y: 100, x: 100, g: 'group' }], color: '#DB1374' },
];
shallowWrapper = shallow(<AreaChart configs={mockConfig} areaChart={mockData} />);
expect(shallowWrapper.find('AutoSizer')).toHaveLength(1);
expect(shallowWrapper.find('ChartHolder')).toHaveLength(0);
});

it('should render a chartHolder if no data given', () => {
const mockData = [{ key: 'uniqueSourceIps', value: [], color: '#DB1374' }];
shallowWrapper = shallow(<AreaChart configs={mockConfig} areaChart={mockData} />);
expect(shallowWrapper.find('AutoSizer')).toHaveLength(0);
expect(shallowWrapper.find('ChartHolder')).toHaveLength(1);
});
});
82 changes: 41 additions & 41 deletions x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ import {
} from '@elastic/charts';
import { getOr, get } from 'lodash/fp';
import {
ChartConfigsData,
ChartSeriesData,
ChartHolder,
getSeriesStyle,
WrappedByAutoSizer,
getTheme,
ChartSeriesConfigs,
browserTimezone,
chartDefaultSettings,
getChartHeight,
getChartWidth,
} from './common';
import { AutoSizer } from '../auto_sizer';

Expand All @@ -52,9 +53,9 @@ const getSeriesLineStyle = (): RecursivePartial<AreaSeriesStyle> => {

// https://ela.st/multi-areaseries
export const AreaChartBaseComponent = React.memo<{
data: ChartConfigsData[];
width: number | null | undefined;
height: number | null | undefined;
data: ChartSeriesData[];
width: string | null | undefined;
height: string | null | undefined;
configs?: ChartSeriesConfigs | undefined;
}>(({ data, ...chartConfigs }) => {
const xTickFormatter = get('configs.axis.xTickFormatter', chartConfigs);
Expand All @@ -68,7 +69,7 @@ export const AreaChartBaseComponent = React.memo<{
return chartConfigs.width && chartConfigs.height ? (
<div style={{ height: chartConfigs.height, width: chartConfigs.width, position: 'relative' }}>
<Chart>
<Settings {...settings} theme={getTheme()} />
<Settings {...settings} />
{data.map(series => {
const seriesKey = series.key;
const seriesSpecId = getSpecId(seriesKey);
Expand All @@ -89,23 +90,15 @@ export const AreaChartBaseComponent = React.memo<{
) : null;
})}

{xTickFormatter ? (
<Axis
id={xAxisId}
position={Position.Bottom}
showOverlappingTicks={false}
tickFormat={xTickFormatter}
tickSize={0}
/>
) : (
<Axis id={xAxisId} position={Position.Bottom} showOverlappingTicks={false} tickSize={0} />
)}
<Axis
id={xAxisId}
position={Position.Bottom}
showOverlappingTicks={false}
tickFormat={xTickFormatter}
tickSize={0}
/>

{yTickFormatter ? (
<Axis id={yAxisId} position={Position.Left} tickSize={0} tickFormat={yTickFormatter} />
) : (
<Axis id={yAxisId} position={Position.Left} tickSize={0} />
)}
<Axis id={yAxisId} position={Position.Left} tickSize={0} tickFormat={yTickFormatter} />
</Chart>
</div>
) : null;
Expand All @@ -114,9 +107,9 @@ export const AreaChartBaseComponent = React.memo<{
AreaChartBaseComponent.displayName = 'AreaChartBaseComponent';

export const AreaChartWithCustomPrompt = React.memo<{
data: ChartConfigsData[] | null | undefined;
height: number | null | undefined;
width: number | null | undefined;
data: ChartSeriesData[] | null | undefined;
height: string | null | undefined;
width: string | null | undefined;
configs?: ChartSeriesConfigs | undefined;
}>(({ data, height, width, configs }) => {
return data != null &&
Expand All @@ -129,28 +122,35 @@ export const AreaChartWithCustomPrompt = React.memo<{
) ? (
<AreaChartBaseComponent height={height} width={width} data={data} configs={configs} />
) : (
<ChartHolder />
<ChartHolder height={height} width={width} />
);
});

AreaChartWithCustomPrompt.displayName = 'AreaChartWithCustomPrompt';

export const AreaChart = React.memo<{
areaChart: ChartConfigsData[] | null | undefined;
areaChart: ChartSeriesData[] | null | undefined;
configs?: ChartSeriesConfigs | undefined;
}>(({ areaChart, configs }) => (
<AutoSizer detectAnyWindowResize={false} content>
{({ measureRef, content: { height, width } }) => (
<WrappedByAutoSizer innerRef={measureRef}>
<AreaChartWithCustomPrompt
data={areaChart}
height={height}
width={width}
configs={configs}
/>
</WrappedByAutoSizer>
)}
</AutoSizer>
));
}>(({ areaChart, configs }) => {
const customHeight = get('customHeight', configs);
const customWidth = get('customWidth', configs);

return get(`0.value.length`, areaChart) ? (
<AutoSizer detectAnyWindowResize={false} content>
{({ measureRef, content: { height, width } }) => (
<WrappedByAutoSizer innerRef={measureRef} height={getChartHeight(customHeight, height)}>
<AreaChartWithCustomPrompt
data={areaChart}
height={getChartHeight(customHeight, height)}
width={getChartWidth(customWidth, width)}
configs={configs}
/>
</WrappedByAutoSizer>
)}
</AutoSizer>
) : (
<ChartHolder height={getChartHeight(customHeight)} width={getChartWidth(customWidth)} />
);
});

AreaChart.displayName = 'AreaChart';
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@
import { shallow, ShallowWrapper } from 'enzyme';
import * as React from 'react';

import { BarChartBaseComponent, BarChartWithCustomPrompt } from './barchart';
import { ChartConfigsData, ChartHolder } from './common';
import { BarChartBaseComponent, BarChartWithCustomPrompt, BarChart } from './barchart';
import { ChartSeriesData, ChartHolder } from './common';
import { BarSeries, ScaleType, Axis } from '@elastic/charts';

jest.mock('@elastic/charts');

const customHeight = '100px';
const customWidth = '120px';
describe('BarChartBaseComponent', () => {
let shallowWrapper: ShallowWrapper;
const mockBarChartData: ChartConfigsData[] = [
const mockBarChartData: ChartSeriesData[] = [
{
key: 'uniqueSourceIps',
value: [{ y: 1714, x: 'uniqueSourceIps', g: 'uniqueSourceIps' }],
Expand All @@ -31,7 +32,7 @@ describe('BarChartBaseComponent', () => {
describe('render', () => {
beforeAll(() => {
shallowWrapper = shallow(
<BarChartBaseComponent height={100} width={120} data={mockBarChartData} />
<BarChartBaseComponent height={customHeight} width={customWidth} data={mockBarChartData} />
);
});

Expand All @@ -54,7 +55,12 @@ describe('BarChartBaseComponent', () => {

beforeAll(() => {
shallowWrapper = shallow(
<BarChartBaseComponent height={100} width={120} data={mockBarChartData} configs={configs} />
<BarChartBaseComponent
height={customHeight}
width={customWidth}
data={mockBarChartData}
configs={configs}
/>
);
});

Expand Down Expand Up @@ -103,7 +109,7 @@ describe('BarChartBaseComponent', () => {
describe('render with default configs if no customized configs given', () => {
beforeAll(() => {
shallowWrapper = shallow(
<BarChartBaseComponent height={100} width={120} data={mockBarChartData} />
<BarChartBaseComponent height={customHeight} width={customWidth} data={mockBarChartData} />
);
});

Expand Down Expand Up @@ -198,7 +204,11 @@ describe.each([
describe('renders barchart', () => {
beforeAll(() => {
shallowWrapper = shallow(
<BarChartWithCustomPrompt height={100} width={120} data={mockBarChartData} />
<BarChartWithCustomPrompt
height={customHeight}
width={customWidth}
data={mockBarChartData}
/>
);
});

Expand Down Expand Up @@ -271,14 +281,49 @@ describe.each([
},
],
],
])('renders prompt', (data: ChartConfigsData[] | [] | null | undefined) => {
])('renders prompt', (data: ChartSeriesData[] | [] | null | undefined) => {
let shallowWrapper: ShallowWrapper;
beforeAll(() => {
shallowWrapper = shallow(<BarChartWithCustomPrompt height={100} width={120} data={data} />);
shallowWrapper = shallow(
<BarChartWithCustomPrompt height={customHeight} width={customWidth} data={data} />
);
});

it('render Chart Holder', () => {
expect(shallowWrapper.find(BarChartBaseComponent)).toHaveLength(0);
expect(shallowWrapper.find(ChartHolder)).toHaveLength(1);
});
});

describe('BarChart', () => {
let shallowWrapper: ShallowWrapper;
const mockConfig = {
series: {
xScaleType: ScaleType.Time,
yScaleType: ScaleType.Linear,
stackAccessors: ['g'],
},
axis: {
xTickFormatter: jest.fn(),
yTickFormatter: jest.fn(),
tickSize: 8,
},
customHeight: 324,
};

it('should render if data exist', () => {
const mockData = [
{ key: 'uniqueSourceIps', value: [{ y: 100, x: 100, g: 'group' }], color: '#DB1374' },
];
shallowWrapper = shallow(<BarChart configs={mockConfig} barChart={mockData} />);
expect(shallowWrapper.find('AutoSizer')).toHaveLength(1);
expect(shallowWrapper.find('ChartHolder')).toHaveLength(0);
});

it('should render a chartHolder if no data given', () => {
const mockData = [{ key: 'uniqueSourceIps', value: [], color: '#DB1374' }];
shallowWrapper = shallow(<BarChart configs={mockConfig} barChart={mockData} />);
expect(shallowWrapper.find('AutoSizer')).toHaveLength(0);
expect(shallowWrapper.find('ChartHolder')).toHaveLength(1);
});
});
Loading

0 comments on commit c878857

Please sign in to comment.