Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Host risk information flyout to Host risk KPI panel #121075

Merged
merged 6 commits into from
Dec 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { loginAndWaitForPage } from '../../tasks/login';
import { HOSTS_URL } from '../../urls/navigation';

describe('RiskyHosts KPI', () => {
it('it renders', () => {
it('renders', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏🏼

loginAndWaitForPage(HOSTS_URL);

cy.get('[data-test-subj="riskyHostsTotal"]').should('have.text', '0 Risky Hosts');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* 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 { mount } from 'enzyme';
import React from 'react';

import { TestProviders } from '../../mock';

import { HoverVisibilityContainer } from '.';

describe('HoverVisibilityContainer', () => {
const targetClass1 = 'Component1';
const targetClass2 = 'Component2';
const Component1 = () => <div className={targetClass1} />;
const Component2 = () => <div className={targetClass2} />;

test('it renders a transparent inspect button by default', () => {
const wrapper = mount(
<TestProviders>
<HoverVisibilityContainer targetClassNames={[targetClass1, targetClass2]}>
<Component1 />
<Component2 />
</HoverVisibilityContainer>
</TestProviders>
);
expect(wrapper.find(`HoverVisibilityContainer`)).toHaveStyleRule('opacity', '0', {
modifier: `.${targetClass1}`,
});
expect(wrapper.find(`HoverVisibilityContainer`)).toHaveStyleRule('opacity', '1', {
modifier: `:hover .${targetClass2}`,
});
});

test('it renders an opaque inspect button when it has mouse focus', () => {
const wrapper = mount(
<TestProviders>
<HoverVisibilityContainer targetClassNames={[targetClass1, targetClass2]}>
<Component1 />
<Component2 />
</HoverVisibilityContainer>
</TestProviders>
);
expect(wrapper.find(`HoverVisibilityContainer`)).toHaveStyleRule('opacity', '1', {
modifier: `:hover .${targetClass1}`,
});
expect(wrapper.find(`HoverVisibilityContainer`)).toHaveStyleRule('opacity', '1', {
modifier: `:hover .${targetClass2}`,
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* 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 styled, { css } from 'styled-components';
import { getOr } from 'lodash/fp';

const StyledDiv = styled.div<{ targetClassNames: string[] }>`
width: 100%;
display: flex;
flex-grow: 1;

> * {
max-width: 100%;
}

${({ targetClassNames }) =>
css`
${targetClassNames.map((cn) => `.${cn}`).join(', ')} {
pointer-events: none;
opacity: 0;
transition: opacity ${(props) => getOr(250, 'theme.eui.euiAnimSpeedNormal', props)} ease;
}

${targetClassNames.map((cn) => `&:hover .${cn}`).join(', ')} {
pointer-events: auto;
opacity: 1;
}
`}
`;

interface HoverVisibilityContainerProps {
show?: boolean;
children: React.ReactNode;
targetClassNames: string[];
}

export const HoverVisibilityContainer = React.memo<HoverVisibilityContainerProps>(
({ show = true, targetClassNames, children }) => {
if (!show) return <>{children}</>;

return (
<StyledDiv data-test-subj="hoverVisibilityContainer" targetClassNames={targetClassNames}>
{children}
</StyledDiv>
);
}
);

HoverVisibilityContainer.displayName = 'HoverVisibilityContainer';
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
import { createStore, State } from '../../store';
import { UpdateQueryParams, upsertQuery } from '../../store/inputs/helpers';

import { InspectButton, InspectButtonContainer, BUTTON_CLASS } from '.';
import { InspectButton } from '.';
import { cloneDeep } from 'lodash/fp';

describe('Inspect Button', () => {
Expand Down Expand Up @@ -103,36 +103,6 @@ describe('Inspect Button', () => {
);
expect(wrapper.find('.euiButtonIcon').get(0).props.disabled).toBe(true);
});

describe('InspectButtonContainer', () => {
machadoum marked this conversation as resolved.
Show resolved Hide resolved
test('it renders a transparent inspect button by default', async () => {
const wrapper = mount(
<TestProviders store={store}>
<InspectButtonContainer>
<InspectButton queryId={newQuery.id} title="My title" />
</InspectButtonContainer>
</TestProviders>
);

expect(wrapper.find(`InspectButtonContainer`)).toHaveStyleRule('opacity', '0', {
modifier: `.${BUTTON_CLASS}`,
});
});

test('it renders an opaque inspect button when it has mouse focus', async () => {
const wrapper = mount(
<TestProviders store={store}>
<InspectButtonContainer>
<InspectButton queryId={newQuery.id} title="My title" />
</InspectButtonContainer>
</TestProviders>
);

expect(wrapper.find(`InspectButtonContainer`)).toHaveStyleRule('opacity', '1', {
modifier: `:hover .${BUTTON_CLASS}`,
});
});
});
});

describe('Modal Inspect - happy path', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,50 +6,34 @@
*/

import { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui';
import { getOr, omit } from 'lodash/fp';
import { omit } from 'lodash/fp';
import React, { useCallback } from 'react';
import { connect, ConnectedProps } from 'react-redux';
import styled, { css } from 'styled-components';

import { inputsSelectors, State } from '../../store';
import { InputsModelId } from '../../store/inputs/constants';
import { inputsActions } from '../../store/inputs';

import { HoverVisibilityContainer } from '../hover_visibility_container';

import { ModalInspectQuery } from './modal';
import * as i18n from './translations';

export const BUTTON_CLASS = 'inspectButtonComponent';

export const InspectButtonContainer = styled.div<{ show?: boolean }>`
width: 100%;
display: flex;
flex-grow: 1;

> * {
max-width: 100%;
}

.${BUTTON_CLASS} {
pointer-events: none;
opacity: 0;
transition: opacity ${(props) => getOr(250, 'theme.eui.euiAnimSpeedNormal', props)} ease;
}

${({ show }) =>
show &&
css`
&:hover .${BUTTON_CLASS} {
pointer-events: auto;
opacity: 1;
}
`}
`;

InspectButtonContainer.displayName = 'InspectButtonContainer';
interface InspectButtonContainerProps {
show?: boolean;
children: React.ReactNode;
}

InspectButtonContainer.defaultProps = {
show: true,
};
export const InspectButtonContainer: React.FC<InspectButtonContainerProps> = ({
machadoum marked this conversation as resolved.
Show resolved Hide resolved
children,
show = true,
}) => (
<HoverVisibilityContainer show={show} targetClassNames={[BUTTON_CLASS]}>
{children}
</HoverVisibilityContainer>
);

interface OwnProps {
compact?: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,8 +263,13 @@ export const AlertsHistogramPanel = memo<AlertsHistogramPanelProps>(
);

return (
<InspectButtonContainer data-test-subj="alerts-histogram-panel" show={!isInitialLoading}>
<KpiPanel height={PANEL_HEIGHT} hasBorder paddingSize={paddingSize}>
<InspectButtonContainer show={!isInitialLoading}>
<KpiPanel
height={PANEL_HEIGHT}
hasBorder
paddingSize={paddingSize}
data-test-subj="alerts-histogram-panel"
>
<HeaderSection
id={uniqueQueryId}
title={titleText}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,14 @@ describe('HostRiskScore', () => {
context
);
});

it("doesn't render background-color when hideBackgroundColor is true", () => {
const { queryByTestId } = render(
<TestProviders>
<HostRiskScore severity={HostRiskSeverity.critical} hideBackgroundColor />
</TestProviders>
);

expect(queryByTestId('host-risk-score')).toHaveStyleRule('background-color', undefined);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,31 @@ const HOST_RISK_SEVERITY_COLOUR = {
Critical: euiLightVars.euiColorDanger,
};

const HostRiskBadge = styled.div<{ $severity: HostRiskSeverity }>`
${({ theme, $severity }) => css`
const HostRiskBadge = styled.div<{ $severity: HostRiskSeverity; $hideBackgroundColor: boolean }>`
${({ theme, $severity, $hideBackgroundColor }) => css`
width: fit-content;
padding-right: ${theme.eui.paddingSizes.s};
padding-left: ${theme.eui.paddingSizes.xs};

${($severity === 'Critical' || $severity === 'High') &&
!$hideBackgroundColor &&
css`
background-color: ${transparentize(theme.eui.euiColorDanger, 0.2)};
border-radius: 999px; // pill shaped
`};
`}
`;

export const HostRiskScore: React.FC<{ severity: HostRiskSeverity }> = ({ severity }) => (
<HostRiskBadge color={euiLightVars.euiColorDanger} $severity={severity}>
export const HostRiskScore: React.FC<{
severity: HostRiskSeverity;
hideBackgroundColor?: boolean;
}> = ({ severity, hideBackgroundColor = false }) => (
<HostRiskBadge
color={euiLightVars.euiColorDanger}
$severity={severity}
$hideBackgroundColor={hideBackgroundColor}
data-test-subj="host-risk-score"
>
<EuiHealth className="eui-alignMiddle" color={HOST_RISK_SEVERITY_COLOUR[severity]}>
{severity}
</EuiHealth>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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, fireEvent } from '@testing-library/react';
import React from 'react';
import { HostRiskInformation } from '.';
import { TestProviders } from '../../../common/mock';

describe('Host Risk Flyout', () => {
it('renders', () => {
const { queryByTestId } = render(<HostRiskInformation />);

expect(queryByTestId('open-risk-information-flyout')).toBeInTheDocument();
});

it('opens and displays table with 5 rows', () => {
const NUMBER_OF_ROWS = 1 + 5; // 1 header row + 5 severity rows
const { getByTestId, queryByTestId, queryAllByRole } = render(
<TestProviders>
<HostRiskInformation />
</TestProviders>
);

fireEvent.click(getByTestId('open-risk-information-flyout'));

expect(queryByTestId('risk-information-table')).toBeInTheDocument();
expect(queryAllByRole('row')).toHaveLength(NUMBER_OF_ROWS);
});
});
Loading