[Cloud Security] kick off the work on the DistributionBar component (#188509)

## Summary

Contributes to:
- https://github.com/elastic/security-team/issues/9954

The PR contains the base for the `DistributionBar` component to be used
in the new Entity Flyout Insights.

Not included:
- badges per distribution with the number of documents and pretty names
- on hover interaction

## Screenshots
<img width="980" alt="Screenshot 2024-07-17 at 15 13 48"
src="https://github.com/user-attachments/assets/f2ca53ee-c054-4923-aa3f-7dd4017754cb">

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Maxim Kholod 2024-07-19 10:38:01 +02:00 committed by GitHub
parent 253266256a
commit 6e73444ca3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 249 additions and 0 deletions

1
.github/CODEOWNERS vendored
View file

@ -738,6 +738,7 @@ x-pack/plugins/security @elastic/kibana-security
x-pack/packages/security/plugin_types_common @elastic/kibana-security
x-pack/packages/security/plugin_types_public @elastic/kibana-security
x-pack/packages/security/plugin_types_server @elastic/kibana-security
x-pack/packages/security-solution/distribution_bar @elastic/kibana-cloud-security-posture
x-pack/plugins/security_solution_ess @elastic/security-solution
x-pack/packages/security-solution/features @elastic/security-threat-hunting-explore
x-pack/test/cases_api_integration/common/plugins/security_solution @elastic/response-ops

View file

@ -749,6 +749,7 @@
"@kbn/security-plugin-types-common": "link:x-pack/packages/security/plugin_types_common",
"@kbn/security-plugin-types-public": "link:x-pack/packages/security/plugin_types_public",
"@kbn/security-plugin-types-server": "link:x-pack/packages/security/plugin_types_server",
"@kbn/security-solution-distribution-bar": "link:x-pack/packages/security-solution/distribution_bar",
"@kbn/security-solution-ess": "link:x-pack/plugins/security_solution_ess",
"@kbn/security-solution-features": "link:x-pack/packages/security-solution/features",
"@kbn/security-solution-fixtures-plugin": "link:x-pack/test/cases_api_integration/common/plugins/security_solution",

View file

@ -1470,6 +1470,8 @@
"@kbn/security-plugin-types-public/*": ["x-pack/packages/security/plugin_types_public/*"],
"@kbn/security-plugin-types-server": ["x-pack/packages/security/plugin_types_server"],
"@kbn/security-plugin-types-server/*": ["x-pack/packages/security/plugin_types_server/*"],
"@kbn/security-solution-distribution-bar": ["x-pack/packages/security-solution/distribution_bar"],
"@kbn/security-solution-distribution-bar/*": ["x-pack/packages/security-solution/distribution_bar/*"],
"@kbn/security-solution-ess": ["x-pack/plugins/security_solution_ess"],
"@kbn/security-solution-ess/*": ["x-pack/plugins/security_solution_ess/*"],
"@kbn/security-solution-features": ["x-pack/packages/security-solution/features"],

View file

@ -0,0 +1,9 @@
/*
* 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.
*/
export { DistributionBar } from './src/distribution_bar';
export type { DistributionBarProps } from './src/distribution_bar';

View file

@ -0,0 +1,12 @@
/*
* 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.
*/
module.exports = {
preset: '@kbn/test',
roots: ['<rootDir>/x-pack/packages/security-solution/distribution_bar'],
rootDir: '../../../..',
};

View file

@ -0,0 +1,5 @@
{
"type": "shared-browser",
"id": "@kbn/security-solution-distribution-bar",
"owner": "@elastic/kibana-cloud-security-posture"
}

View file

@ -0,0 +1,7 @@
{
"name": "@kbn/security-solution-distribution-bar",
"private": true,
"version": "1.0.0",
"license": "Elastic License 2.0",
"sideEffects": false
}

View file

@ -0,0 +1,73 @@
/*
* 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 React from 'react';
import { euiThemeVars } from '@kbn/ui-theme';
import { EuiTitle, EuiSpacer } from '@elastic/eui';
import { DistributionBar as DistributionBarComponent } from '..';
const mockStatsFindings = [
{
key: 'passed',
count: 90,
color: euiThemeVars.euiColorVis0,
},
{
key: 'failed',
count: 10,
color: euiThemeVars.euiColorVis9,
},
];
const mockStatsAlerts = [
{
key: 'low',
count: 30,
color: euiThemeVars.euiColorVis0,
},
{
key: 'medium',
count: 30,
color: euiThemeVars.euiColorVis5,
},
{
key: 'high',
count: 10,
color: euiThemeVars.euiColorVis7,
},
{
key: 'critical',
count: 10,
color: euiThemeVars.euiColorVis9,
},
];
export default {
title: 'DistributionBar',
description: 'Distribution Bar',
};
export const DistributionBar = () => {
return [
<EuiTitle size={'xs'}>
<h4>{'Findings'}</h4>
</EuiTitle>,
<EuiSpacer size={'s'} />,
<DistributionBarComponent stats={mockStatsFindings} />,
<EuiSpacer size={'m'} />,
<EuiTitle size={'xs'}>
<h4>{'Alerts'}</h4>
</EuiTitle>,
<EuiSpacer size={'s'} />,
<DistributionBarComponent stats={mockStatsAlerts} />,
<EuiSpacer size={'m'} />,
<EuiTitle size={'xs'}>
<h4>{'Empty state'}</h4>
</EuiTitle>,
<EuiSpacer size={'s'} />,
<DistributionBarComponent stats={[]} />,
];
};

View file

@ -0,0 +1,41 @@
/*
* 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 React from 'react';
import { render } from '@testing-library/react';
import { DistributionBar } from '..';
describe('DistributionBar', () => {
it('should render', () => {
const stats = [
{
key: 'passed',
count: 90,
color: 'green',
},
{
key: 'failed',
count: 10,
color: 'red',
},
];
const { container } = render(<DistributionBar stats={stats} />);
expect(container).toBeInTheDocument();
expect(container.querySelectorAll('span').length).toEqual(stats.length);
});
it('should render empty bar', () => {
const { container } = render(
<DistributionBar data-test-subj={'distribution-bar'} stats={[]} />
);
expect(container).toBeInTheDocument();
expect(container.querySelectorAll('span').length).toEqual(1);
expect(
container.querySelector('[data-test-subj="distribution-bar__emptyBar"]')
).toBeInTheDocument();
});
});

View file

@ -0,0 +1,75 @@
/*
* 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 React from 'react';
import { EuiFlexGroup, useEuiTheme } from '@elastic/eui';
import { css } from '@emotion/react';
/** DistributionBar component props */
export interface DistributionBarProps {
/** distribution data points */
stats: Array<{ key: string; count: number; color: string }>;
/** data-test-subj used for querying the component in tests */
['data-test-subj']?: string;
}
export interface EmptyBarProps {
['data-test-subj']?: string;
}
const styles = {
base: css`
border-radius: 2px;
height: 5px;
`,
};
const EmptyBar: React.FC<EmptyBarProps> = ({ 'data-test-subj': dataTestSubj }) => {
const { euiTheme } = useEuiTheme();
const emptyBarStyle = [
styles.base,
css`
background-color: ${euiTheme.colors.lightShade};
flex: 1;
`,
];
return <span css={emptyBarStyle} data-test-subj={`${dataTestSubj}__emptyBar`} />;
};
/**
* Security Solution DistributionBar component.
* Shows visual representation of distribution of stats, such as alerts by criticality or misconfiguration findings by evaluation result.
*/
export const DistributionBar: React.FC<DistributionBarProps> = React.memo(function DistributionBar(
props
) {
const { euiTheme } = useEuiTheme();
const { stats, 'data-test-subj': dataTestSubj } = props;
const parts = stats.map((stat) => {
const partStyle = [
styles.base,
css`
background-color: ${stat.color};
flex: ${stat.count};
`,
];
return <span key={stat.key} css={partStyle} />;
});
return (
<EuiFlexGroup
css={css`
gap: ${euiTheme.size.xxs};
`}
data-test-subj={dataTestSubj}
>
{parts.length ? parts : <EmptyBar data-test-subj={dataTestSubj} />}
</EuiFlexGroup>
);
});

View file

@ -0,0 +1,19 @@
{
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
"jest",
"node",
"react",
"@emotion/react/types/css-prop",
"@testing-library/jest-dom",
"@testing-library/react",
]
},
"include": ["**/*.ts", "**/*.tsx"],
"kbn_references": [
"@kbn/ui-theme",
],
"exclude": ["target/**/*"]
}

View file

@ -6190,6 +6190,10 @@
version "0.0.0"
uid ""
"@kbn/security-solution-distribution-bar@link:x-pack/packages/security-solution/distribution_bar":
version "0.0.0"
uid ""
"@kbn/security-solution-ess@link:x-pack/plugins/security_solution_ess":
version "0.0.0"
uid ""