Skip to content

Commit

Permalink
[SIEM] Replace Eui chart with elastic charts for siem kpis (#36660) (#…
Browse files Browse the repository at this point in the history
…37317)

* move charts to separate components

* replace areachart

* apply custom styles

* customize barchart color

* customize color for areachart

* move reusable functions into common

* exchange x & y value in barchart dataset

* replace pure component with react memo and upgrade enzyme adapter
  • Loading branch information
angorayc authored May 29, 2019
1 parent c62833b commit 3de093d
Show file tree
Hide file tree
Showing 15 changed files with 1,060 additions and 1,213 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@
"dedent": "^0.7.0",
"delete-empty": "^2.0.0",
"enzyme": "^3.7.0",
"enzyme-adapter-react-16": "^1.9.0",
"enzyme-adapter-react-16": "^1.10.0",
"enzyme-adapter-utils": "^1.10.0",
"enzyme-to-json": "^3.3.4",
"eslint": "^5.16.0",
Expand Down
2 changes: 1 addition & 1 deletion x-pack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
"del": "^3.0.0",
"dotenv": "2.0.0",
"enzyme": "^3.7.0",
"enzyme-adapter-react-16": "^1.9.0",
"enzyme-adapter-react-16": "^1.10.0",
"enzyme-adapter-utils": "^1.10.0",
"enzyme-to-json": "^3.3.4",
"execa": "^1.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { mount, ReactWrapper } from 'enzyme';
import * as React from 'react';

import { AreaChartBaseComponent, AreaChartWithCustomPrompt } from './areachart';
import { AreaChartData } from '.';
import { AreaChartData } from './common';

describe('AreaChartBaseComponent', () => {
let wrapper: ReactWrapper;
Expand Down Expand Up @@ -38,16 +38,8 @@ describe('AreaChartBaseComponent', () => {
wrapper = mount(<AreaChartBaseComponent height={100} width={120} data={mockAreaChartData} />);
});

it('should render two area series', () => {
expect(wrapper.find('EuiAreaSeries')).toHaveLength(2);
});

it('should render a customized x-asix', () => {
expect(wrapper.find('EuiXAxis')).toHaveLength(1);
});

it('should render a customized y-asix', () => {
expect(wrapper.find('EuiYAxis')).toHaveLength(1);
it('should render Chart', () => {
expect(wrapper.find('Chart')).toHaveLength(1);
});
});

Expand All @@ -59,7 +51,7 @@ describe('AreaChartBaseComponent', () => {
});

it('should not render without height and width', () => {
expect(wrapper.find('SeriesChart')).toHaveLength(0);
expect(wrapper.find('Chart')).toHaveLength(0);
});
});
});
Expand Down Expand Up @@ -113,7 +105,7 @@ describe('AreaChartWithCustomPrompt', () => {
});

it('render AreaChartBaseComponent', () => {
expect(wrapper.find('[data-test-subj="stat-area-chart"]').first()).toHaveLength(1);
expect(wrapper.find('Chart')).toHaveLength(1);
expect(wrapper.find('ChartHolder')).toHaveLength(0);
});
});
Expand Down Expand Up @@ -195,7 +187,7 @@ describe('AreaChartWithCustomPrompt', () => {
});

it('render Chart Holder', () => {
expect(wrapper.find('[data-test-subj="stat-area-chart"]')).toHaveLength(0);
expect(wrapper.find('Chart')).toHaveLength(0);
expect(wrapper.find('ChartHolder')).toHaveLength(1);
});
});
Expand Down
134 changes: 134 additions & 0 deletions x-pack/plugins/siem/public/components/charts/areachart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* 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 React from 'react';
import {
Axis,
AreaSeries,
Chart,
getAxisId,
getSpecId,
Position,
ScaleType,
} from '@elastic/charts';
import '@elastic/charts/dist/style.css';
import {
AreaChartData,
ChartHolder,
getSeriesStyle,
numberFormatter,
WrappedByAutoSizer,
} from './common';
import { AutoSizer } from '../auto_sizer';

const dateFormatter = (d: string) => {
return d.toLocaleString().split('T')[0];
};

const getSeriesLineStyle = (color: string | undefined) => {
return color
? {
area: {
fill: color,
opacity: 0.04,
visible: true,
},
line: {
stroke: color,
strokeWidth: 1,
visible: true,
},
border: {
visible: false,
strokeWidth: 1,
stroke: color,
},
point: {
visible: false,
radius: 0.2,
stroke: color,
strokeWidth: 1,
opacity: 1,
},
}
: undefined;
};

export const AreaChartBaseComponent = React.memo<{
data: AreaChartData[];
width: number | null | undefined;
height: number | null | undefined;
}>(({ data, ...chartConfigs }) => {
return chartConfigs.width && chartConfigs.height ? (
<div style={{ height: chartConfigs.height, width: chartConfigs.width, position: 'relative' }}>
<Chart>
{data.map(series => {
const seriesKey = series.key;
const seriesSpecId = getSpecId(seriesKey);
return series.value != null ? (
<AreaSeries
id={seriesSpecId}
key={seriesKey}
name={series.key.replace('Histogram', '')}
data={series.value}
xScaleType={ScaleType.Ordinal}
yScaleType={ScaleType.Linear}
xAccessor="x"
yAccessors={['y']}
areaSeriesStyle={getSeriesLineStyle(series.color)}
customSeriesColors={getSeriesStyle(seriesKey, series.color)}
/>
) : null;
})}

<Axis
id={getAxisId(`group-${data[0].key}-x`)}
position={Position.Bottom}
showOverlappingTicks={false}
tickFormat={dateFormatter}
tickSize={0}
/>
<Axis
id={getAxisId(`group-${data[0].key}-y`)}
position={Position.Left}
tickSize={0}
tickFormat={numberFormatter}
/>
</Chart>
</div>
) : null;
});

export const AreaChartWithCustomPrompt = React.memo<{
data: AreaChartData[] | null | undefined;
height: number | null | undefined;
width: number | null | undefined;
}>(({ data, height, width }) => {
return data != null &&
data.length &&
data.every(
({ value }) =>
value != null &&
value.length > 0 &&
value.every(chart => chart.x != null && chart.y != null)
) ? (
<AreaChartBaseComponent height={height} width={width} data={data} />
) : (
<ChartHolder />
);
});

export const AreaChart = React.memo<{ areaChart: AreaChartData[] | null | undefined }>(
({ areaChart }) => (
<AutoSizer detectAnyWindowResize={false} content>
{({ measureRef, content: { height, width } }) => (
<WrappedByAutoSizer innerRef={measureRef}>
<AreaChartWithCustomPrompt data={areaChart} height={height} width={width} />
</WrappedByAutoSizer>
)}
</AutoSizer>
)
);
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,19 @@ import { mount, ReactWrapper } from 'enzyme';
import * as React from 'react';

import { BarChartBaseComponent, BarChartWithCustomPrompt } from './barchart';
import { BarChartData } from '.';
import { BarChartData } from './common';

describe('BarChartBaseComponent', () => {
let wrapper: ReactWrapper;
const mockBarChartData: BarChartData[] = [
{ key: 'uniqueSourceIps', value: [{ x: 1714, y: 'uniqueSourceIps' }], color: '#DB1374' },
{
key: 'uniqueSourceIps',
value: [{ y: 1714, x: 'uniqueSourceIps', g: 'uniqueSourceIps' }],
color: '#DB1374',
},
{
key: 'uniqueDestinationIps',
value: [{ x: 2354, y: 'uniqueDestinationIps' }],
value: [{ y: 2354, x: 'uniqueDestinationIps', g: 'uniqueDestinationIps' }],
color: '#490092',
},
];
Expand All @@ -26,16 +30,8 @@ describe('BarChartBaseComponent', () => {
wrapper = mount(<BarChartBaseComponent height={100} width={120} data={mockBarChartData} />);
});

it('should render two area series', () => {
expect(wrapper.find('EuiBarSeries')).toHaveLength(2);
});

it('should render a customized x-asix', () => {
expect(wrapper.find('EuiXAxis')).toHaveLength(1);
});

it('should render a customized y-asix', () => {
expect(wrapper.find('EuiYAxis')).toHaveLength(1);
it('should render two bar series', () => {
expect(wrapper.find('Chart')).toHaveLength(1);
});
});

Expand All @@ -45,38 +41,38 @@ describe('BarChartBaseComponent', () => {
});

it('should not render without height and width', () => {
expect(wrapper.find('SeriesChart')).toHaveLength(0);
expect(wrapper.find('Chart')).toHaveLength(0);
});
});
});

describe.each([
[
[
{ key: 'uniqueSourceIps', value: [{ x: 1714, y: 'uniqueSourceIps' }], color: '#DB1374' },
{ key: 'uniqueSourceIps', value: [{ y: 1714, x: 'uniqueSourceIps' }], color: '#DB1374' },
{
key: 'uniqueDestinationIps',
value: [{ x: 2354, y: 'uniqueDestinationIps' }],
value: [{ y: 2354, x: 'uniqueDestinationIps' }],
color: '#490092',
},
],
],
[
[
{ key: 'uniqueSourceIps', value: [{ x: 1714, y: '' }], color: '#DB1374' },
{ key: 'uniqueSourceIps', value: [{ y: 1714, x: '' }], color: '#DB1374' },
{
key: 'uniqueDestinationIps',
value: [{ x: 2354, y: '' }],
value: [{ y: 2354, x: '' }],
color: '#490092',
},
],
],
[
[
{ key: 'uniqueSourceIps', value: [{ x: 1714, y: 'uniqueSourceIps' }], color: '#DB1374' },
{ key: 'uniqueSourceIps', value: [{ y: 1714, x: 'uniqueSourceIps' }], color: '#DB1374' },
{
key: 'uniqueDestinationIps',
value: [{ x: 0, y: 'uniqueDestinationIps' }],
value: [{ y: 0, x: 'uniqueDestinationIps' }],
color: '#490092',
},
],
Expand All @@ -91,7 +87,7 @@ describe.each([
});

it('render BarChartBaseComponent', () => {
expect(wrapper.find('[data-test-subj="stat-bar-chart"]').first()).toHaveLength(1);
expect(wrapper.find('Chart')).toHaveLength(1);
expect(wrapper.find('ChartHolder')).toHaveLength(0);
});
});
Expand Down Expand Up @@ -131,30 +127,30 @@ describe.each([
],
[
[
{ key: 'uniqueSourceIps', value: [{ x: 0, y: 'uniqueSourceIps' }], color: '#DB1374' },
{ key: 'uniqueSourceIps', value: [{ y: 0, x: 'uniqueSourceIps' }], color: '#DB1374' },
{
key: 'uniqueDestinationIps',
value: [{ x: 0, y: 'uniqueDestinationIps' }],
value: [{ y: 0, x: 'uniqueDestinationIps' }],
color: '#490092',
},
],
],
[
[
{ key: 'uniqueSourceIps', value: [{ x: null, y: 'uniqueSourceIps' }], color: '#DB1374' },
{ key: 'uniqueSourceIps', value: [{ y: null, x: 'uniqueSourceIps' }], color: '#DB1374' },
{
key: 'uniqueDestinationIps',
value: [{ x: 2354, y: 'uniqueDestinationIps' }],
value: [{ y: 2354, x: 'uniqueDestinationIps' }],
color: '#490092',
},
],
],
[
[
{ key: 'uniqueSourceIps', value: [{ x: null, y: 'uniqueSourceIps' }], color: '#DB1374' },
{ key: 'uniqueSourceIps', value: [{ y: null, x: 'uniqueSourceIps' }], color: '#DB1374' },
{
key: 'uniqueDestinationIps',
value: [{ x: null, y: 'uniqueDestinationIps' }],
value: [{ y: null, x: 'uniqueDestinationIps' }],
color: '#490092',
},
],
Expand All @@ -166,7 +162,7 @@ describe.each([
});

it('render Chart Holder', () => {
expect(wrapper.find('[data-test-subj="stat-bar-chart"]')).toHaveLength(0);
expect(wrapper.find('Chart')).toHaveLength(0);
expect(wrapper.find('ChartHolder')).toHaveLength(1);
});
});
Loading

0 comments on commit 3de093d

Please sign in to comment.