[Security Solution][Investigations] Fix overlapping tooltips (#117874)

* feat: allow to pass `tooltipPosition` to `DefaultDraggable`

* fix: prevent suricata field name and google url tooltips from overlapping

* test: add test for passing the tooltipPosition

* chore: distinguish between type imports and regular imports

* test: update suricata signature snapshots

* test: make sure that suricata signature tooltips do not overlap

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Jan Monschke 2021-11-10 15:47:00 +01:00 committed by GitHub
parent 0b5a434d19
commit d0e30f5475
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 77 additions and 4 deletions

View file

@ -20,3 +20,18 @@ export const formatMitreAttackDescription = (mitre: Mitre[]) => {
)
.join('');
};
export const elementsOverlap = ($element1: JQuery<HTMLElement>, $element2: JQuery<HTMLElement>) => {
const rectA = $element1[0].getBoundingClientRect();
const rectB = $element2[0].getBoundingClientRect();
// If they don't overlap horizontally, they don't overlap
if (rectA.right < rectB.left || rectB.right < rectA.left) {
return false;
} else if (rectA.bottom < rectB.top || rectB.bottom < rectA.top) {
// If they don't overlap vertically, they don't overlap
return false;
} else {
return true;
}
};

View file

@ -5,12 +5,16 @@
* 2.0.
*/
import { elementsOverlap } from '../../helpers/rules';
import {
TIMELINE_ROW_RENDERERS_DISABLE_ALL_BTN,
TIMELINE_ROW_RENDERERS_MODAL_CLOSE_BUTTON,
TIMELINE_ROW_RENDERERS_MODAL_ITEMS_CHECKBOX,
TIMELINE_ROW_RENDERERS_SEARCHBOX,
TIMELINE_SHOW_ROW_RENDERERS_GEAR,
TIMELINE_ROW_RENDERERS_SURICATA_SIGNATURE,
TIMELINE_ROW_RENDERERS_SURICATA_SIGNATURE_TOOLTIP,
TIMELINE_ROW_RENDERERS_SURICATA_LINK_TOOLTIP,
} from '../../screens/timeline';
import { cleanKibana } from '../../tasks/common';
@ -81,4 +85,22 @@ describe('Row renderers', () => {
cy.wait('@updateTimeline').its('response.statusCode').should('eq', 200);
});
describe('Suricata', () => {
it('Signature tooltips do not overlap', () => {
// Hover the signature to show the tooltips
cy.get(TIMELINE_ROW_RENDERERS_SURICATA_SIGNATURE)
.parents('.euiPopover__anchor')
.trigger('mouseover');
cy.get(TIMELINE_ROW_RENDERERS_SURICATA_LINK_TOOLTIP).then(($googleLinkTooltip) => {
cy.get(TIMELINE_ROW_RENDERERS_SURICATA_SIGNATURE_TOOLTIP).then(($signatureTooltip) => {
expect(
elementsOverlap($googleLinkTooltip, $signatureTooltip),
'tooltips do not overlap'
).to.equal(false);
});
});
});
});
});

View file

@ -245,6 +245,12 @@ export const TIMELINE_ROW_RENDERERS_MODAL_ITEMS_CHECKBOX = `${TIMELINE_ROW_RENDE
export const TIMELINE_ROW_RENDERERS_SEARCHBOX = `${TIMELINE_ROW_RENDERERS_MODAL} input[type="search"]`;
export const TIMELINE_ROW_RENDERERS_SURICATA_SIGNATURE = `${TIMELINE_ROW_RENDERERS_MODAL} [data-test-subj="render-content-suricata.eve.alert.signature"]`;
export const TIMELINE_ROW_RENDERERS_SURICATA_LINK_TOOLTIP = `[data-test-subj="externalLinkTooltip"]`;
export const TIMELINE_ROW_RENDERERS_SURICATA_SIGNATURE_TOOLTIP = `[data-test-subj="suricata.eve.alert.signature-tooltip"]`;
export const TIMELINE_SHOW_ROW_RENDERERS_GEAR = '[data-test-subj="show-row-renderers-gear"]';
export const TIMELINE_TABS = '[data-test-subj="timeline"] .euiTabs';

View file

@ -7,6 +7,7 @@
import { shallow } from 'enzyme';
import React from 'react';
import { EuiToolTip } from '@elastic/eui';
import { DRAGGABLE_KEYBOARD_INSTRUCTIONS_NOT_DRAGGING_SCREEN_READER_ONLY } from '../drag_and_drop/translations';
import { TestProviders } from '../../mock';
@ -326,5 +327,21 @@ describe('draggables', () => {
expect(wrapper.find('[data-test-subj="some-field-tooltip"]').first().exists()).toBe(false);
});
test('it uses the specified tooltipPosition', () => {
const wrapper = mount(
<TestProviders>
<DraggableBadge
contextId="context-id"
eventId="event-id"
field="some-field"
value="some value"
tooltipPosition="top"
/>
</TestProviders>
);
expect(wrapper.find(EuiToolTip).first().props().position).toEqual('top');
});
});
});

View file

@ -5,7 +5,8 @@
* 2.0.
*/
import { EuiBadge, EuiToolTip, IconType } from '@elastic/eui';
import { EuiBadge, EuiToolTip } from '@elastic/eui';
import type { IconType, ToolTipPositions } from '@elastic/eui';
import React, { useCallback, useMemo } from 'react';
import styled from 'styled-components';
@ -29,6 +30,7 @@ export interface DefaultDraggableType {
children?: React.ReactNode;
timelineId?: string;
tooltipContent?: React.ReactNode;
tooltipPosition?: ToolTipPositions;
}
/**
@ -60,11 +62,13 @@ export const Content = React.memo<{
children?: React.ReactNode;
field: string;
tooltipContent?: React.ReactNode;
tooltipPosition?: ToolTipPositions;
value?: string | null;
}>(({ children, field, tooltipContent, value }) =>
}>(({ children, field, tooltipContent, tooltipPosition, value }) =>
!tooltipContentIsExplicitlyNull(tooltipContent) ? (
<EuiToolTip
data-test-subj={`${field}-tooltip`}
position={tooltipPosition}
content={getDefaultWhenTooltipIsUnspecified({ tooltipContent, field })}
>
<>{children ? children : value}</>
@ -88,6 +92,7 @@ Content.displayName = 'Content';
* @param children - defaults to displaying `value`, this allows an arbitrary visualization to be displayed in lieu of the default behavior
* @param tooltipContent - defaults to displaying `field`, pass `null` to
* prevent a tooltip from being displayed, or pass arbitrary content
* @param tooltipPosition - defaults to eui's default tooltip position
* @param queryValue - defaults to `value`, this query overrides the `queryMatch.value` used by the `DataProvider` that represents the data
* @param hideTopN - defaults to `false`, when true, the option to aggregate this field will be hidden
*/
@ -102,6 +107,7 @@ export const DefaultDraggable = React.memo<DefaultDraggableType>(
children,
timelineId,
tooltipContent,
tooltipPosition,
queryValue,
}) => {
const dataProviderProp: DataProvider = useMemo(
@ -128,11 +134,16 @@ export const DefaultDraggable = React.memo<DefaultDraggableType>(
<Provider dataProvider={dataProvider} />
</DragEffects>
) : (
<Content field={field} tooltipContent={tooltipContent} value={value}>
<Content
field={field}
tooltipContent={tooltipContent}
tooltipPosition={tooltipPosition}
value={value}
>
{children}
</Content>
),
[children, field, tooltipContent, value]
[children, field, tooltipContent, tooltipPosition, value]
);
if (value == null) return null;

View file

@ -26,6 +26,7 @@ exports[`SuricataSignature rendering it renders the default SuricataSignature 1`
data-test-subj="draggable-signature-link"
field="suricata.eve.alert.signature"
id="suricata-signature-default-draggable-test-doc-id-123-suricata.eve.alert.signature"
tooltipPosition="bottom"
value="ET SCAN ATTACK Hello"
>
<div>

View file

@ -130,6 +130,7 @@ export const SuricataSignature = React.memo<{
id={`suricata-signature-default-draggable-${contextId}-${id}-${SURICATA_SIGNATURE_FIELD_NAME}`}
isDraggable={isDraggable}
value={signature}
tooltipPosition="bottom"
>
<div>
<GoogleLink link={signature}>