[SIEM] Replace Eui chart with elastic charts for siem kpis (#36660)
* 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
This commit is contained in:
parent
90fdb149e0
commit
ac3eb8566b
|
@ -349,7 +349,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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
134
x-pack/plugins/siem/public/components/charts/areachart.tsx
Normal file
134
x-pack/plugins/siem/public/components/charts/areachart.tsx
Normal file
|
@ -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>
|
||||
)
|
||||
);
|
|
@ -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',
|
||||
},
|
||||
];
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -45,7 +41,7 @@ describe('BarChartBaseComponent', () => {
|
|||
});
|
||||
|
||||
it('should not render without height and width', () => {
|
||||
expect(wrapper.find('SeriesChart')).toHaveLength(0);
|
||||
expect(wrapper.find('Chart')).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -53,30 +49,30 @@ describe('BarChartBaseComponent', () => {
|
|||
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',
|
||||
},
|
||||
],
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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',
|
||||
},
|
||||
],
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
111
x-pack/plugins/siem/public/components/charts/barchart.tsx
Normal file
111
x-pack/plugins/siem/public/components/charts/barchart.tsx
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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 {
|
||||
Chart,
|
||||
BarSeries,
|
||||
Axis,
|
||||
Position,
|
||||
getSpecId,
|
||||
ScaleType,
|
||||
Settings,
|
||||
mergeWithDefaultTheme,
|
||||
PartialTheme,
|
||||
} from '@elastic/charts';
|
||||
import { getAxisId } from '@elastic/charts';
|
||||
import {
|
||||
BarChartData,
|
||||
WrappedByAutoSizer,
|
||||
ChartHolder,
|
||||
numberFormatter,
|
||||
SeriesType,
|
||||
getSeriesStyle,
|
||||
} from './common';
|
||||
import { AutoSizer } from '../auto_sizer';
|
||||
|
||||
const getTheme = () => {
|
||||
const theme: PartialTheme = {
|
||||
scales: {
|
||||
barsPadding: 0.5,
|
||||
},
|
||||
};
|
||||
return mergeWithDefaultTheme(theme);
|
||||
};
|
||||
|
||||
export const BarChartBaseComponent = React.memo<{
|
||||
data: BarChartData[];
|
||||
width: number | null | undefined;
|
||||
height: number | null | undefined;
|
||||
}>(({ data, ...chartConfigs }) => {
|
||||
return chartConfigs.width && chartConfigs.height ? (
|
||||
<Chart>
|
||||
<Settings rotation={90} theme={getTheme()} />
|
||||
{data.map(series => {
|
||||
const barSeriesKey = series.key;
|
||||
const barSeriesSpecId = getSpecId(barSeriesKey);
|
||||
const seriesType = SeriesType.BAR;
|
||||
return (
|
||||
<BarSeries
|
||||
id={barSeriesSpecId}
|
||||
key={barSeriesKey}
|
||||
name={series.key}
|
||||
xScaleType={ScaleType.Ordinal}
|
||||
yScaleType={ScaleType.Linear}
|
||||
xAccessor="x"
|
||||
yAccessors={['y']}
|
||||
splitSeriesAccessors={['g']}
|
||||
data={series.value!}
|
||||
stackAccessors={['y']}
|
||||
customSeriesColors={getSeriesStyle(barSeriesKey, series.color, seriesType)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
<Axis
|
||||
id={getAxisId(`stat-items-barchart-${data[0].key}-x`)}
|
||||
position={Position.Bottom}
|
||||
tickSize={0}
|
||||
tickFormat={numberFormatter}
|
||||
/>
|
||||
<Axis
|
||||
id={getAxisId(`stat-items-barchart-${data[0].key}-y`)}
|
||||
position={Position.Left}
|
||||
tickSize={0}
|
||||
/>
|
||||
</Chart>
|
||||
) : null;
|
||||
});
|
||||
|
||||
export const BarChartWithCustomPrompt = React.memo<{
|
||||
data: BarChartData[] | null | undefined;
|
||||
height: number | null | undefined;
|
||||
width: number | null | undefined;
|
||||
}>(({ data, height, width }) => {
|
||||
return data &&
|
||||
data.length &&
|
||||
data.some(
|
||||
({ value }) =>
|
||||
value != null && value.length > 0 && value.every(chart => chart.y != null && chart.y > 0)
|
||||
) ? (
|
||||
<BarChartBaseComponent height={height} width={width} data={data} />
|
||||
) : (
|
||||
<ChartHolder />
|
||||
);
|
||||
});
|
||||
|
||||
export const BarChart = React.memo<{ barChart: BarChartData[] | null | undefined }>(
|
||||
({ barChart }) => (
|
||||
<AutoSizer detectAnyWindowResize={false} content>
|
||||
{({ measureRef, content: { height, width } }) => (
|
||||
<WrappedByAutoSizer innerRef={measureRef}>
|
||||
<BarChartWithCustomPrompt height={height} width={width} data={barChart} />
|
||||
</WrappedByAutoSizer>
|
||||
)}
|
||||
</AutoSizer>
|
||||
)
|
||||
);
|
81
x-pack/plugins/siem/public/components/charts/common.tsx
Normal file
81
x-pack/plugins/siem/public/components/charts/common.tsx
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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 { EuiFlexGroup, EuiText, EuiFlexItem } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { CustomSeriesColorsMap, DataSeriesColorsValues, getSpecId } from '@elastic/charts';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
const FlexGroup = styled(EuiFlexGroup)`
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
export const ChartHolder = () => (
|
||||
<FlexGroup justifyContent="center" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" textAlign="center" color="subdued">
|
||||
{i18n.translate('xpack.siem.chart.dataNotAvailableTitle', {
|
||||
defaultMessage: 'Chart Data Not Available',
|
||||
})}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</FlexGroup>
|
||||
);
|
||||
|
||||
export interface AreaChartData {
|
||||
key: string;
|
||||
value: ChartData[] | [] | null;
|
||||
color?: string | undefined;
|
||||
}
|
||||
|
||||
export interface ChartData {
|
||||
x: number | string | null;
|
||||
y: number | string | null;
|
||||
y0?: number;
|
||||
g?: number | string;
|
||||
}
|
||||
|
||||
export interface BarChartData {
|
||||
key: string;
|
||||
value: [ChartData] | [] | null;
|
||||
color?: string | undefined;
|
||||
}
|
||||
|
||||
export const WrappedByAutoSizer = styled.div`
|
||||
height: 100px;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
z-index: 100;
|
||||
}
|
||||
`;
|
||||
|
||||
export const numberFormatter = (value: string | number) => {
|
||||
return value.toLocaleString && value.toLocaleString();
|
||||
};
|
||||
|
||||
export enum SeriesType {
|
||||
BAR = 'bar',
|
||||
AREA = 'area',
|
||||
LINE = 'line',
|
||||
}
|
||||
|
||||
export const getSeriesStyle = (
|
||||
seriesKey: string,
|
||||
color: string | undefined,
|
||||
seriesType?: SeriesType
|
||||
) => {
|
||||
if (!color) return undefined;
|
||||
const customSeriesColors: CustomSeriesColorsMap = new Map();
|
||||
const dataSeriesColorValues: DataSeriesColorsValues = {
|
||||
colorValues: seriesType === SeriesType.BAR ? [seriesKey] : [],
|
||||
specId: getSpecId(seriesKey),
|
||||
};
|
||||
|
||||
customSeriesColors.set(dataSeriesColorValues, color);
|
||||
|
||||
return customSeriesColors;
|
||||
};
|
|
@ -7,19 +7,12 @@
|
|||
import { EuiFlexGroup } from '@elastic/eui';
|
||||
import { get, getOr } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
import { EuiLoadingSpinner } from '@elastic/eui';
|
||||
import { EuiFlexItem } from '@elastic/eui';
|
||||
import { KpiHostsData } from '../../../../graphql/types';
|
||||
import {
|
||||
AreaChartData,
|
||||
BarChartData,
|
||||
StatItem,
|
||||
StatItems,
|
||||
StatItemsComponent,
|
||||
StatItemsProps,
|
||||
} from '../../../stat_items';
|
||||
import { StatItem, StatItems, StatItemsComponent, StatItemsProps } from '../../../stat_items';
|
||||
import * as i18n from './translations';
|
||||
import { BarChartData, AreaChartData } from '../../../charts/common';
|
||||
|
||||
interface KpiHostsProps {
|
||||
data: KpiHostsData;
|
||||
|
@ -93,7 +86,7 @@ const fieldTitleMapping: StatItems[] = [
|
|||
},
|
||||
];
|
||||
|
||||
export const KpiHostsComponent = pure<KpiHostsProps>(({ data, loading }) => {
|
||||
export const KpiHostsComponent = React.memo<KpiHostsProps>(({ data, loading }) => {
|
||||
return loading ? (
|
||||
<EuiFlexGroup justifyContent="center" alignItems="center" style={{ minHeight: 247 }}>
|
||||
<EuiFlexItem grow={false}>
|
||||
|
@ -148,8 +141,8 @@ const addValueToBarChart = (fields: StatItem[], data: KpiHostsData): BarChartDat
|
|||
if (fields.length === 0) return [];
|
||||
return fields.reduce((acc: BarChartData[], field: StatItem, idx: number) => {
|
||||
const key: string = get('key', field);
|
||||
const x: number | null = getOr(null, key, data);
|
||||
const y: string = get(`${idx}.name`, fields) || getOr('', `${idx}.description`, fields);
|
||||
const y: number | null = getOr(null, key, data);
|
||||
const x: string = get(`${idx}.name`, fields) || getOr('', `${idx}.description`, fields);
|
||||
|
||||
return acc.concat([
|
||||
{
|
||||
|
@ -158,6 +151,7 @@ const addValueToBarChart = (fields: StatItem[], data: KpiHostsData): BarChartDat
|
|||
{
|
||||
x,
|
||||
y,
|
||||
g: key,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,85 +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 React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
import styled from 'styled-components';
|
||||
import { EuiSeriesChart, EuiAreaSeries, EuiXAxis, EuiYAxis } from '@elastic/eui/lib/experimental';
|
||||
import { AreaChartData, WrappedByAutoSizer, ChartHolder } from '.';
|
||||
import { AutoSizer } from '../auto_sizer';
|
||||
|
||||
export const AreaChartBaseComponent = pure<{
|
||||
data: AreaChartData[];
|
||||
width: number | null | undefined;
|
||||
height: number | null | undefined;
|
||||
}>(({ data, ...chartConfigs }) =>
|
||||
chartConfigs.width && chartConfigs.height ? (
|
||||
<SeriesChart
|
||||
{...chartConfigs}
|
||||
showDefaultAxis={false}
|
||||
xType="ordinal"
|
||||
data-test-subj="stat-area-chart"
|
||||
>
|
||||
{data.map(series => (
|
||||
/**
|
||||
* Placing ts-ignore here for fillOpacity
|
||||
* */
|
||||
// @ts-ignore
|
||||
<EuiAreaSeries
|
||||
key={`stat-items-areachart-${series.key}`}
|
||||
name={series.key.replace('Histogram', '')}
|
||||
// @ts-ignore
|
||||
data={series.value}
|
||||
fillOpacity={0.04}
|
||||
color={series.color}
|
||||
/>
|
||||
))}
|
||||
{/*
|
||||
// @ts-ignore */}
|
||||
<EuiXAxis tickFormat={timestamp => timestamp.split('T')[0]} />
|
||||
{/*
|
||||
// @ts-ignore */}
|
||||
<EuiYAxis />
|
||||
</SeriesChart>
|
||||
) : null
|
||||
);
|
||||
|
||||
export const AreaChartWithCustomPrompt = pure<{
|
||||
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 = pure<{ 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>
|
||||
)
|
||||
);
|
||||
|
||||
const SeriesChart = styled(EuiSeriesChart)`
|
||||
svg .rv-xy-plot__axis__ticks .rv-xy-plot__axis__tick:not(:first-child):not(:last-child) {
|
||||
display: none;
|
||||
}
|
||||
`;
|
|
@ -1,94 +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 React from 'react';
|
||||
import {
|
||||
// @ts-ignore
|
||||
EuiSeriesChartUtils,
|
||||
} from '@elastic/eui';
|
||||
import { pure } from 'recompose';
|
||||
import styled from 'styled-components';
|
||||
import { EuiSeriesChart, EuiBarSeries, EuiXAxis, EuiYAxis } from '@elastic/eui/lib/experimental';
|
||||
import { BarChartData, WrappedByAutoSizer, ChartHolder } from '.';
|
||||
import { AutoSizer } from '../auto_sizer';
|
||||
|
||||
const { SCALE, ORIENTATION } = EuiSeriesChartUtils;
|
||||
const getYaxis = (value: string | number) => {
|
||||
const label = value.toString();
|
||||
const labelLength = 4;
|
||||
return label.length > labelLength ? `${label.slice(0, labelLength)}.` : label;
|
||||
};
|
||||
|
||||
export const BarChartBaseComponent = pure<{
|
||||
data: BarChartData[];
|
||||
width: number | null | undefined;
|
||||
height: number | null | undefined;
|
||||
}>(({ data, ...chartConfigs }) => {
|
||||
return chartConfigs.width && chartConfigs.height ? (
|
||||
// @ts-ignore
|
||||
<SeriesChart
|
||||
yType={SCALE.ORDINAL}
|
||||
orientation={ORIENTATION.HORIZONTAL}
|
||||
showDefaultAxis={false}
|
||||
data-test-subj="stat-bar-chart"
|
||||
{...chartConfigs}
|
||||
>
|
||||
{data.map(series => {
|
||||
return (
|
||||
<EuiBarSeries
|
||||
key={`stat-items-areachart-${series.key}`}
|
||||
name={series.key}
|
||||
// @ts-ignore
|
||||
data={series.value!}
|
||||
color={series.color}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{/*
|
||||
// @ts-ignore */}
|
||||
<EuiXAxis />
|
||||
{/*
|
||||
// @ts-ignore */}
|
||||
<EuiYAxis tickFormat={getYaxis} />
|
||||
</SeriesChart>
|
||||
) : null;
|
||||
});
|
||||
|
||||
export const BarChartWithCustomPrompt = pure<{
|
||||
data: BarChartData[] | null | undefined;
|
||||
height: number | null | undefined;
|
||||
width: number | null | undefined;
|
||||
}>(({ data, height, width }) => {
|
||||
return data &&
|
||||
data.length &&
|
||||
data.some(
|
||||
({ value }) =>
|
||||
value != null && value.length > 0 && value.every(chart => chart.x != null && chart.x > 0)
|
||||
) ? (
|
||||
<BarChartBaseComponent height={height} width={width} data={data} />
|
||||
) : (
|
||||
<ChartHolder />
|
||||
);
|
||||
});
|
||||
|
||||
export const BarChart = pure<{ barChart: BarChartData[] | null | undefined }>(({ barChart }) => (
|
||||
<AutoSizer detectAnyWindowResize={false} content>
|
||||
{({ measureRef, content: { height, width } }) => (
|
||||
<WrappedByAutoSizer innerRef={measureRef}>
|
||||
<BarChartWithCustomPrompt height={height} width={width} data={barChart} />
|
||||
</WrappedByAutoSizer>
|
||||
)}
|
||||
</AutoSizer>
|
||||
));
|
||||
|
||||
const SeriesChart = styled(EuiSeriesChart)`
|
||||
svg
|
||||
.rv-xy-plot__axis--horizontal
|
||||
.rv-xy-plot__axis__ticks
|
||||
.rv-xy-plot__axis__tick:not(:first-child):not(:last-child) {
|
||||
display: none;
|
||||
}
|
||||
`;
|
|
@ -9,8 +9,8 @@ import toJson from 'enzyme-to-json';
|
|||
import * as React from 'react';
|
||||
|
||||
import { StatItemsComponent, StatItemsProps } from '.';
|
||||
import { BarChart } from './barchart';
|
||||
import { AreaChart } from './areachart';
|
||||
import { BarChart } from '../charts/barchart';
|
||||
import { AreaChart } from '../charts/areachart';
|
||||
import { EuiHorizontalRule } from '@elastic/eui';
|
||||
|
||||
describe('Stat Items', () => {
|
||||
|
|
|
@ -11,28 +11,15 @@ import {
|
|||
EuiHorizontalRule,
|
||||
EuiIcon,
|
||||
EuiTitle,
|
||||
IconType,
|
||||
} from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { EuiText } from '@elastic/eui';
|
||||
import { BarChart } from './barchart';
|
||||
import { AreaChart } from './areachart';
|
||||
import { BarChart } from '../charts/barchart';
|
||||
import { AreaChart } from '../charts/areachart';
|
||||
import { getEmptyTagValue } from '../empty_value';
|
||||
|
||||
export const WrappedByAutoSizer = styled.div`
|
||||
height: 100px;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
z-index: 100;
|
||||
}
|
||||
`;
|
||||
|
||||
const FlexGroup = styled(EuiFlexGroup)`
|
||||
height: 100%;
|
||||
`;
|
||||
import { AreaChartData, BarChartData } from '../charts/common';
|
||||
|
||||
const FlexItem = styled(EuiFlexItem)`
|
||||
min-width: 0;
|
||||
|
@ -49,28 +36,10 @@ export interface StatItem {
|
|||
description?: string;
|
||||
value: number | undefined | null;
|
||||
color?: string;
|
||||
icon?: 'storage' | 'cross' | 'check' | 'visMapCoordinate';
|
||||
icon?: IconType;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export interface AreaChartData {
|
||||
key: string;
|
||||
value: ChartData[] | [] | null;
|
||||
color?: string | undefined;
|
||||
}
|
||||
|
||||
export interface ChartData {
|
||||
x: number | string | null;
|
||||
y: number | string | null;
|
||||
y0?: number;
|
||||
}
|
||||
|
||||
export interface BarChartData {
|
||||
key: string;
|
||||
value: [ChartData] | [] | null;
|
||||
color?: string | undefined;
|
||||
}
|
||||
|
||||
export interface StatItems {
|
||||
fields: StatItem[];
|
||||
description?: string;
|
||||
|
@ -85,7 +54,7 @@ export interface StatItemsProps extends StatItems {
|
|||
barChart?: BarChartData[];
|
||||
}
|
||||
|
||||
export const StatItemsComponent = pure<StatItemsProps>(
|
||||
export const StatItemsComponent = React.memo<StatItemsProps>(
|
||||
({ fields, description, key, grow, barChart, areaChart, enableAreaChart, enableBarChart }) => {
|
||||
const isBarChartDataAbailable =
|
||||
barChart &&
|
||||
|
@ -150,13 +119,3 @@ export const StatItemsComponent = pure<StatItemsProps>(
|
|||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const ChartHolder = () => (
|
||||
<FlexGroup justifyContent="center" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s" textAlign="center" color="subdued">
|
||||
Chart Data Not Available
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</FlexGroup>
|
||||
);
|
||||
|
|
|
@ -17,7 +17,7 @@ import { createFilter } from '../helpers';
|
|||
import { QueryTemplateProps } from '../query_template';
|
||||
|
||||
import { kpiHostsQuery } from './index.gql_query';
|
||||
import { ChartData } from '../../components/stat_items';
|
||||
import { ChartData } from '../../components/charts/common';
|
||||
|
||||
export interface KpiHostsArgs {
|
||||
id: string;
|
||||
|
|
120
yarn.lock
120
yarn.lock
|
@ -4660,6 +4660,22 @@ aggregate-error@^1.0.0:
|
|||
string.prototype.padstart "^3.0.0"
|
||||
symbol.prototype.description "^1.0.0"
|
||||
|
||||
airbnb-prop-types@^2.13.2:
|
||||
version "2.13.2"
|
||||
resolved "https://registry.yarnpkg.com/airbnb-prop-types/-/airbnb-prop-types-2.13.2.tgz#43147a5062dd2a4a5600e748a47b64004cc5f7fc"
|
||||
integrity sha512-2FN6DlHr6JCSxPPi25EnqGaXC4OC3/B3k1lCd6MMYrZ51/Gf/1qDfaR+JElzWa+Tl7cY2aYOlsYJGFeQyVHIeQ==
|
||||
dependencies:
|
||||
array.prototype.find "^2.0.4"
|
||||
function.prototype.name "^1.1.0"
|
||||
has "^1.0.3"
|
||||
is-regex "^1.0.4"
|
||||
object-is "^1.0.1"
|
||||
object.assign "^4.1.0"
|
||||
object.entries "^1.1.0"
|
||||
prop-types "^15.7.2"
|
||||
prop-types-exact "^1.2.0"
|
||||
react-is "^16.8.6"
|
||||
|
||||
ajv-errors@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.0.tgz#ecf021fa108fd17dfb5e6b383f2dd233e31ffc59"
|
||||
|
@ -5403,6 +5419,14 @@ array-unique@^0.3.2:
|
|||
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
|
||||
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
|
||||
|
||||
array.prototype.find@^2.0.4:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.1.0.tgz#630f2eaf70a39e608ac3573e45cf8ccd0ede9ad7"
|
||||
integrity sha512-Wn41+K1yuO5p7wRZDl7890c3xvv5UBrfVXTVIe28rSQb6LS0fZMDrQB6PAcxQFRFy6vJTLDc3A2+3CjQdzVKRg==
|
||||
dependencies:
|
||||
define-properties "^1.1.3"
|
||||
es-abstract "^1.13.0"
|
||||
|
||||
array.prototype.flat@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.1.tgz#812db8f02cad24d3fab65dd67eabe3b8903494a4"
|
||||
|
@ -10391,18 +10415,19 @@ env-variable@0.0.x:
|
|||
resolved "https://registry.yarnpkg.com/env-variable/-/env-variable-0.0.5.tgz#913dd830bef11e96a039c038d4130604eba37f88"
|
||||
integrity sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA==
|
||||
|
||||
enzyme-adapter-react-16@^1.9.0:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.9.1.tgz#6d49a3a31c3a0fccf527610f31b837e0f307128a"
|
||||
integrity sha512-Egzogv1y77DUxdnq/CyHxLHaNxmSSKDDSDNNB/EiAXCZVFXdFibaNy2uUuRQ1n24T2m6KH/1Rw16XDRq+1yVEg==
|
||||
enzyme-adapter-react-16@^1.10.0:
|
||||
version "1.13.2"
|
||||
resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.13.2.tgz#8a574d7cbbef7ef0cab2022e9bfc12aeaebb7ae5"
|
||||
integrity sha512-h0neTuAAFfQUgEZ+PPHVIMDFJ9+CGafI8AjojNlSVh4Fd1pLDgtl2OeVkm4yKF7RSgzrPAwugq4JW8Jjo2iRJA==
|
||||
dependencies:
|
||||
enzyme-adapter-utils "^1.10.0"
|
||||
function.prototype.name "^1.1.0"
|
||||
enzyme-adapter-utils "^1.12.0"
|
||||
has "^1.0.3"
|
||||
object.assign "^4.1.0"
|
||||
object.values "^1.1.0"
|
||||
prop-types "^15.6.2"
|
||||
react-is "^16.7.0"
|
||||
prop-types "^15.7.2"
|
||||
react-is "^16.8.6"
|
||||
react-test-renderer "^16.0.0-0"
|
||||
semver "^5.7.0"
|
||||
|
||||
enzyme-adapter-utils@^1.10.0:
|
||||
version "1.10.0"
|
||||
|
@ -10415,6 +10440,18 @@ enzyme-adapter-utils@^1.10.0:
|
|||
prop-types "^15.6.2"
|
||||
semver "^5.6.0"
|
||||
|
||||
enzyme-adapter-utils@^1.12.0:
|
||||
version "1.12.0"
|
||||
resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.12.0.tgz#96e3730d76b872f593e54ce1c51fa3a451422d93"
|
||||
integrity sha512-wkZvE0VxcFx/8ZsBw0iAbk3gR1d9hK447ebnSYBf95+r32ezBq+XDSAvRErkc4LZosgH8J7et7H7/7CtUuQfBA==
|
||||
dependencies:
|
||||
airbnb-prop-types "^2.13.2"
|
||||
function.prototype.name "^1.1.0"
|
||||
object.assign "^4.1.0"
|
||||
object.fromentries "^2.0.0"
|
||||
prop-types "^15.7.2"
|
||||
semver "^5.6.0"
|
||||
|
||||
enzyme-to-json@^3.3.4:
|
||||
version "3.3.5"
|
||||
resolved "https://registry.yarnpkg.com/enzyme-to-json/-/enzyme-to-json-3.3.5.tgz#f8eb82bd3d5941c9d8bc6fd9140030777d17d0af"
|
||||
|
@ -10474,7 +10511,7 @@ error@7.0.2, error@^7.0.0, error@^7.0.2:
|
|||
string-template "~0.2.1"
|
||||
xtend "~4.0.0"
|
||||
|
||||
es-abstract@^1.10.0, es-abstract@^1.11.0, es-abstract@^1.12.0, es-abstract@^1.5.0:
|
||||
es-abstract@^1.10.0, es-abstract@^1.11.0, es-abstract@^1.12.0, es-abstract@^1.13.0, es-abstract@^1.5.0:
|
||||
version "1.13.0"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9"
|
||||
integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==
|
||||
|
@ -19294,9 +19331,9 @@ object-is@^1.0.1:
|
|||
integrity sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=
|
||||
|
||||
object-keys@^1.0.11, object-keys@^1.0.12:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.0.tgz#11bd22348dd2e096a045ab06f6c85bcc340fa032"
|
||||
integrity sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg==
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
|
||||
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
|
||||
|
||||
object-keys@^1.0.6:
|
||||
version "1.0.11"
|
||||
|
@ -19345,7 +19382,7 @@ object.defaults@^1.1.0:
|
|||
for-own "^1.0.0"
|
||||
isobject "^3.0.0"
|
||||
|
||||
object.entries@^1.0.4:
|
||||
object.entries@^1.0.4, object.entries@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.0.tgz#2024fc6d6ba246aee38bdb0ffd5cfbcf371b7519"
|
||||
integrity sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==
|
||||
|
@ -20832,6 +20869,15 @@ prompts@^2.0.1:
|
|||
kleur "^3.0.2"
|
||||
sisteransi "^1.0.0"
|
||||
|
||||
prop-types-exact@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/prop-types-exact/-/prop-types-exact-1.2.0.tgz#825d6be46094663848237e3925a98c6e944e9869"
|
||||
integrity sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA==
|
||||
dependencies:
|
||||
has "^1.0.3"
|
||||
object.assign "^4.1.0"
|
||||
reflect.ownkeys "^0.2.0"
|
||||
|
||||
prop-types@15.5.8:
|
||||
version "15.5.8"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.8.tgz#6b7b2e141083be38c8595aa51fc55775c7199394"
|
||||
|
@ -21712,15 +21758,10 @@ react-is@^16.3.1:
|
|||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.1.tgz#d624c4650d2c65dbd52c72622bbf389435d9776e"
|
||||
integrity sha512-xpb0PpALlFWNw/q13A+1aHeyJyLYCg0/cCHPUA43zYluZuIPHaHL3k8OBsTgQtxqW0FhyDEMvi8fZ/+7+r4OSQ==
|
||||
|
||||
react-is@^16.7.0:
|
||||
version "16.7.0"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.7.0.tgz#c1bd21c64f1f1364c6f70695ec02d69392f41bfa"
|
||||
integrity sha512-Z0VRQdF4NPDoI0tsXVMLkJLiwEBa+RP66g0xDHxgxysxSoCUccSten4RTF/UFvZF1dZvZ9Zu1sx+MDXwcOR34g==
|
||||
|
||||
react-is@^16.8.1, react-is@^16.8.2:
|
||||
version "16.8.2"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.2.tgz#09891d324cad1cb0c1f2d91f70a71a4bee34df0f"
|
||||
integrity sha512-D+NxhSR2HUCjYky1q1DwpNUD44cDpUXzSmmFyC3ug1bClcU/iDNy0YNn1iwme28fn+NFhpA13IndOd42CrFb+Q==
|
||||
react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.2, react-is@^16.8.6:
|
||||
version "16.8.6"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
|
||||
integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==
|
||||
|
||||
react-is@^16.8.4:
|
||||
version "16.8.5"
|
||||
|
@ -22040,7 +22081,17 @@ react-syntax-highlighter@^8.0.1:
|
|||
prismjs "^1.8.4"
|
||||
refractor "^2.4.1"
|
||||
|
||||
react-test-renderer@^16.0.0-0, react-test-renderer@^16.8.0:
|
||||
react-test-renderer@^16.0.0-0:
|
||||
version "16.8.6"
|
||||
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.8.6.tgz#188d8029b8c39c786f998aa3efd3ffe7642d5ba1"
|
||||
integrity sha512-H2srzU5IWYT6cZXof6AhUcx/wEyJddQ8l7cLM/F7gDXYyPr4oq+vCIxJYXVGhId1J706sqziAjuOEjyNkfgoEw==
|
||||
dependencies:
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.2"
|
||||
react-is "^16.8.6"
|
||||
scheduler "^0.13.6"
|
||||
|
||||
react-test-renderer@^16.8.0:
|
||||
version "16.8.2"
|
||||
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.8.2.tgz#3ce0bf12aa211116612fda01a886d6163c9c459b"
|
||||
integrity sha512-gsd4NoOaYrZD2R8zi+CBV9wTGMsGhE2bRe4wvenGy0WcLJgdPscRZDDz+kmLjY+/5XpYC8yRR/v4CScgYfGyoQ==
|
||||
|
@ -22536,6 +22587,11 @@ redux@^4.0.1:
|
|||
loose-envify "^1.4.0"
|
||||
symbol-observable "^1.2.0"
|
||||
|
||||
reflect.ownkeys@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460"
|
||||
integrity sha1-dJrO7H8/34tj+SegSAnpDFwLNGA=
|
||||
|
||||
refractor@^2.4.1:
|
||||
version "2.8.0"
|
||||
resolved "https://registry.yarnpkg.com/refractor/-/refractor-2.8.0.tgz#29d7b2254e823edd2e3e476af286af1c11472bfa"
|
||||
|
@ -23519,15 +23575,7 @@ sax@>=0.6.0, sax@^1.2.1, sax@^1.2.4, sax@~1.2.1, sax@~1.2.4:
|
|||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
|
||||
|
||||
scheduler@^0.13.2:
|
||||
version "0.13.2"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.2.tgz#969eaee2764a51d2e97b20a60963b2546beff8fa"
|
||||
integrity sha512-qK5P8tHS7vdEMCW5IPyt8v9MJOHqTrOUgPXib7tqm9vh834ibBX5BNhwkplX/0iOzHW5sXyluehYfS9yrkz9+w==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
scheduler@^0.13.3, scheduler@^0.13.6:
|
||||
scheduler@^0.13.2, scheduler@^0.13.3, scheduler@^0.13.6:
|
||||
version "0.13.6"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.6.tgz#466a4ec332467b31a91b9bf74e5347072e4cd889"
|
||||
integrity sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==
|
||||
|
@ -23670,10 +23718,10 @@ semver@^5.5.1:
|
|||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477"
|
||||
integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==
|
||||
|
||||
semver@^5.6.0:
|
||||
version "5.6.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
|
||||
integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
|
||||
semver@^5.6.0, semver@^5.7.0:
|
||||
version "5.7.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b"
|
||||
integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==
|
||||
|
||||
semver@~5.3.0:
|
||||
version "5.3.0"
|
||||
|
|
Loading…
Reference in a new issue