Skip to content

Commit

Permalink
[SecuritySolution] Cases by status (#130670)
Browse files Browse the repository at this point in the history
* init cases by status

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* cases by status

* tickLabel

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* unit tests

* unit tests

* rm unused dependency

* disable dashboard

* dependency

* enable dashboard

* styling

* styling

* unit tests

* styling

* unit tests

* disable dashboard

* fix types

* fix types

* unit tests

* unit test

* unit tests

* unit tests

* unit tests

* unit tests

* unit tests

* update i18n

* remove cases api hack

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
angorayc and kibanamachine committed Apr 27, 2022
1 parent f5f0356 commit 56d0112
Show file tree
Hide file tree
Showing 12 changed files with 664 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import { Chart, BarSeries, Axis, ScaleType } from '@elastic/charts';
import { Chart, BarSeries, Axis, ScaleType, AxisStyle } from '@elastic/charts';
import { mount, ReactWrapper, shallow, ShallowWrapper } from 'enzyme';
import React from 'react';

Expand Down Expand Up @@ -166,13 +166,43 @@ describe('BarChartBaseComponent', () => {

describe('render with customized configs', () => {
const mockNumberFormatter = jest.fn();
const mockXAxisStyle = {
tickLine: {
size: 0,
},
tickLabel: {
padding: 16,
fontSize: 10.5,
},
} as Partial<AxisStyle>;
const mockYAxisStyle = {
tickLine: {
size: 0,
},
tickLabel: {
padding: 16,
fontSize: 14,
},
} as Partial<AxisStyle>;
const configs = {
series: {
xScaleType: ScaleType.Ordinal,
yScaleType: ScaleType.Linear,
barSeriesStyle: {
rect: {
widthPixel: 22,
opacity: 1,
},
},
},
axis: {
yTickFormatter: mockNumberFormatter,
bottom: {
style: mockXAxisStyle,
},
left: {
style: mockYAxisStyle,
},
},
};

Expand Down Expand Up @@ -203,12 +233,22 @@ describe('BarChartBaseComponent', () => {
);
});

it('should render BarSeries with given barSeriesStyle', () => {
expect(shallowWrapper.find(BarSeries).first().prop('barSeriesStyle')).toEqual(
configs.series.barSeriesStyle
);
});

it('should render xAxis with given tick formatter', () => {
expect(shallowWrapper.find(Axis).first().prop('tickFormat')).toBeUndefined();
});

it('should render xAxis style', () => {
expect(shallowWrapper.find(Axis).first().prop('style')).toEqual(mockXAxisStyle);
});

it('should render yAxis with given tick formatter', () => {
expect(shallowWrapper.find(Axis).last().prop('tickFormat')).toEqual(mockNumberFormatter);
expect(shallowWrapper.find(Axis).last().prop('style')).toEqual(mockYAxisStyle);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,34 @@ export const BarChartBaseComponent = ({
...deepmerge(get('configs.settings', chartConfigs), { theme }),
};

const xAxisStyle = useMemo(
() =>
deepmerge(
{
tickLine: {
size: tickSize,
},
},
getOr({}, 'configs.axis.bottom.style', chartConfigs)
),
[chartConfigs, tickSize]
);

const yAxisStyle = useMemo(
() =>
deepmerge(
{
tickLine: {
size: tickSize,
},
},
getOr({}, 'configs.axis.left.style', chartConfigs)
),
[chartConfigs, tickSize]
);

const xAxisLabelFormat = get('configs.axis.bottom.labelFormat', chartConfigs);

return chartConfigs.width && chartConfigs.height ? (
<Chart>
<Settings {...settings} showLegend={settings.showLegend && !forceHiddenLegend} />
Expand All @@ -106,6 +134,7 @@ export const BarChartBaseComponent = ({
data={series.value ?? []}
stackAccessors={get('configs.series.stackAccessors', chartConfigs)}
color={series.color ? series.color : undefined}
barSeriesStyle={get('configs.series.barSeriesStyle', chartConfigs)}
/>
) : null;
})}
Expand All @@ -114,22 +143,15 @@ export const BarChartBaseComponent = ({
id={xAxisId}
position={Position.Bottom}
showOverlappingTicks={false}
style={{
tickLine: {
size: tickSize,
},
}}
style={xAxisStyle}
tickFormat={xTickFormatter}
labelFormat={xAxisLabelFormat}
/>

<Axis
id={yAxisId}
position={Position.Left}
style={{
tickLine: {
size: tickSize,
},
}}
style={yAxisStyle}
tickFormat={yTickFormatter}
title={yAxisTitle}
/>
Expand All @@ -143,7 +165,7 @@ export const BarChartBase = React.memo(BarChartBaseComponent);

BarChartBase.displayName = 'BarChartBase';

interface BarChartComponentProps {
export interface BarChartComponentProps {
barChart: ChartSeriesData[] | null | undefined;
configs?: ChartSeriesConfigs | undefined;
stackByField?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
TickFormatter,
Position,
BrushEndListener,
AxisStyle,
BarSeriesStyle,
} from '@elastic/charts';
import React, { useMemo } from 'react';
import styled from 'styled-components';
Expand Down Expand Up @@ -45,11 +47,20 @@ export interface ChartSeriesConfigs {
xScaleType?: ScaleType | undefined;
yScaleType?: ScaleType | undefined;
stackAccessors?: string[] | undefined;
barSeriesStyle?: Partial<BarSeriesStyle>;
};
axis?: {
xTickFormatter?: TickFormatter | undefined;
yTickFormatter?: TickFormatter | undefined;
tickSize?: number | undefined;
left?: {
style?: Partial<AxisStyle>;
labelFormat?: (d: unknown) => string;
};
bottom?: {
style?: Partial<AxisStyle>;
labelFormat?: (d: unknown) => string;
};
};
yAxisTitle?: string | undefined;
settings?: SettingsProps;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,12 @@ export const AlertsByStatus = ({ signalIndexName }: AlertsByStatusProps) => {
[]
);

const openCount = donutData?.open?.total ?? 0;
const acknowledgedCount = donutData?.acknowledged?.total ?? 0;
const closedCount = donutData?.closed?.total ?? 0;

const totalAlerts =
loading || donutData == null
? 0
: (donutData?.open?.total ?? 0) +
(donutData?.acknowledged?.total ?? 0) +
(donutData?.closed?.total ?? 0);
loading || donutData == null ? 0 : openCount + acknowledgedCount + closedCount;

const fillColor: FillColor = useCallback((d: ShapeTreeNode) => {
return chartConfigs.find((cfg) => cfg.label === d.dataName)?.color ?? emptyDonutColor;
Expand Down Expand Up @@ -152,20 +152,17 @@ export const AlertsByStatus = ({ signalIndexName }: AlertsByStatusProps) => {
<>
<EuiFlexGroup justifyContent="center" gutterSize="none">
<EuiFlexItem grow={false}>
<EuiText className="eui-textCenter" size="s">
{loading ? (
<EuiSpacer size="l" />
) : (
{totalAlerts !== 0 && (
<EuiText className="eui-textCenter" size="s">
<>
<b>
<FormattedCount count={totalAlerts} />
</b>
<> </>
<small>{ALERTS(totalAlerts)}</small>
</>
)}
</EuiText>

</EuiText>
)}
<EuiSpacer size="l" />
<EuiFlexGroup justifyContent="center">
<StyledFlexItem key="alerts-status-open" grow={false}>
Expand All @@ -174,8 +171,8 @@ export const AlertsByStatus = ({ signalIndexName }: AlertsByStatusProps) => {
fillColor={fillColor}
height={donutHeight}
label={STATUS_OPEN}
title={<ChartLabel count={donutData?.open?.total ?? 0} />}
totalCount={donutData?.open?.total ?? 0}
title={<ChartLabel count={openCount} />}
totalCount={openCount}
/>
</StyledFlexItem>
<StyledFlexItem key="alerts-status-acknowledged" grow={false}>
Expand All @@ -184,8 +181,8 @@ export const AlertsByStatus = ({ signalIndexName }: AlertsByStatusProps) => {
fillColor={fillColor}
height={donutHeight}
label={STATUS_ACKNOWLEDGED}
title={<ChartLabel count={donutData?.acknowledged?.total ?? 0} />}
totalCount={donutData?.acknowledged?.total ?? 0}
title={<ChartLabel count={acknowledgedCount} />}
totalCount={acknowledgedCount}
/>
</StyledFlexItem>
<StyledFlexItem key="alerts-status-closed" grow={false}>
Expand All @@ -194,8 +191,8 @@ export const AlertsByStatus = ({ signalIndexName }: AlertsByStatusProps) => {
fillColor={fillColor}
height={donutHeight}
label={STATUS_CLOSED}
title={<ChartLabel count={donutData?.closed?.total ?? 0} />}
totalCount={donutData?.closed?.total ?? 0}
title={<ChartLabel count={closedCount} />}
totalCount={closedCount}
/>
</StyledFlexItem>
</EuiFlexGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { render, screen } from '@testing-library/react';
import React from 'react';
import { BarChartComponentProps } from '../../../../common/components/charts/barchart';
import { useQueryToggle } from '../../../../common/containers/query_toggle';
import { TestProviders } from '../../../../common/mock';
import { CasesByStatus } from './cases_by_status';
jest.mock('../../../../common/components/link_to');
jest.mock('../../../../common/containers/query_toggle');
jest.mock('./use_cases_by_status', () => ({
useCasesByStatus: jest.fn().mockReturnValue({
closed: 1,
inProgress: 2,
isLoading: false,
open: 3,
totalCounts: 6,
updatedAt: new Date('2022-04-08T12:00:00.000Z').valueOf(),
}),
}));
jest.mock('../../../../common/lib/kibana', () => {
const actual = jest.requireActual('../../../../common/lib/kibana');
return {
...actual,
useNavigation: jest.fn().mockReturnValue({
getAppUrl: jest.fn(),
navigateTo: jest.fn(),
}),
};
});
jest.mock('../../../../common/components/charts/barchart', () => ({
BarChart: jest.fn((props: BarChartComponentProps) => <div data-test-subj="barChart" />),
}));

const mockSetToggle = jest.fn();
(useQueryToggle as jest.Mock).mockReturnValue({
toggleStatus: true,
setToggleStatus: mockSetToggle,
});

describe('CasesByStatus', () => {
beforeEach(() => {
jest.clearAllMocks();
});

test('renders title', () => {
render(
<TestProviders>
<CasesByStatus />
</TestProviders>
);
expect(screen.getByTestId('header-section-title')).toHaveTextContent('Cases');
});

test('renders toggleQuery', () => {
render(
<TestProviders>
<CasesByStatus />
</TestProviders>
);
expect(screen.getByTestId('query-toggle-header')).toBeInTheDocument();
});

test('renders BarChart', () => {
render(
<TestProviders>
<CasesByStatus />
</TestProviders>
);
expect(screen.getByTestId('chart-wrapper')).toBeInTheDocument();
expect(screen.queryByTestId('bar-chart-mask')).not.toBeInTheDocument();
});

test('collapses content', () => {
(useQueryToggle as jest.Mock).mockReturnValueOnce({
toggleStatus: false,
setToggleStatus: mockSetToggle,
});
render(
<TestProviders>
<CasesByStatus />
</TestProviders>
);

expect(screen.queryByTestId('chart-wrapper')).not.toBeInTheDocument();
});
});
Loading

0 comments on commit 56d0112

Please sign in to comment.