Skip to content

Commit

Permalink
[Security Solution] Disable renderer hover actions in the Rules previ…
Browse files Browse the repository at this point in the history
…ew flyout (#141546)

## [Security Solution] Disable hover actions on renderers in the Rules preview flyout

This PR addresses issue <#141026> by disabling hover actions on renderers in the  _Rules preview_ flyout, per the Before and After screenshots below:

### Before

![before](https://user-images.githubusercontent.com/4459398/191833235-ed9974d8-3d31-4da1-8db6-e5c500003f6b.png)

_Above: Before the fix, actions were displayed when hovering over a rendered field in the Rules preview flyout_

### After

![after](https://user-images.githubusercontent.com/4459398/191833522-427649ab-9670-48df-8a87-1980fe8e4070.png)

_Above: After the fix, actions are NOT displayed when hovering over a rendered field in the Rules preview flyout_
  • Loading branch information
andrew-goldstein authored Sep 27, 2022
1 parent 504d8c4 commit f571f80
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,24 @@
* 2.0.
*/

import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { shallow } from 'enzyme';
import React from 'react';
import type { DraggableStateSnapshot, DraggingStyle } from 'react-beautiful-dnd';
import { waitFor } from '@testing-library/react';

import '../../mock/match_media';
import { TimelineId } from '../../../../common/types';
import { mockBrowserFields } from '../../containers/source/mock';
import { TestProviders } from '../../mock';
import { mockDataProviders } from '../../../timelines/components/timeline/data_providers/mock/mock_data_providers';
import { ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID } from '../../../timelines/components/row_renderers_browser/constants';
import { DragDropContextWrapper } from './drag_drop_context_wrapper';
import { ConditionalPortal, DraggableWrapper, getStyle } from './draggable_wrapper';
import {
ConditionalPortal,
disableHoverActions,
DraggableWrapper,
getStyle,
} from './draggable_wrapper';
import { useMountAppended } from '../../utils/use_mount_appended';

jest.mock('../../lib/kibana');
Expand All @@ -27,6 +35,26 @@ jest.mock('@elastic/eui', () => {
};
});

const timelineIdsWithHoverActions = [
undefined,
TimelineId.active,
TimelineId.alternateTest,
TimelineId.casePage,
TimelineId.detectionsPage,
TimelineId.detectionsRulesDetailsPage,
TimelineId.hostsPageEvents,
TimelineId.hostsPageSessions,
TimelineId.kubernetesPageSessions,
TimelineId.networkPageEvents,
TimelineId.test,
TimelineId.usersPageEvents,
];

const timelineIdsNoHoverActions = [
TimelineId.rulePreview,
ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID,
];

describe('DraggableWrapper', () => {
const dataProvider = mockDataProviders[0];
const message = 'draggable wrapper content';
Expand All @@ -36,6 +64,15 @@ describe('DraggableWrapper', () => {
jest.useFakeTimers();
});

afterEach(() => {
const portal = document.querySelector('[data-euiportal="true"]');
if (portal != null) {
portal.innerHTML = '';
}

jest.useRealTimers();
});

describe('rendering', () => {
test('it renders against the snapshot', () => {
const wrapper = shallow(
Expand Down Expand Up @@ -103,6 +140,56 @@ describe('DraggableWrapper', () => {
expect(wrapper.find('[data-test-subj="hover-actions-copy-button"]').exists()).toBe(true);
});
});

timelineIdsWithHoverActions.forEach((timelineId) => {
test(`it renders hover actions (by default) when 'isDraggable' is false and timelineId is '${timelineId}'`, async () => {
const isDraggable = false;

const { container } = render(
<TestProviders>
<DragDropContextWrapper browserFields={mockBrowserFields}>
<DraggableWrapper
dataProvider={dataProvider}
isDraggable={isDraggable}
render={() => message}
timelineId={timelineId}
/>
</DragDropContextWrapper>
</TestProviders>
);

fireEvent.mouseEnter(container.querySelector('[data-test-subj="withHoverActionsButton"]')!);

await waitFor(() => {
expect(screen.getByTestId('hover-actions-copy-button')).toBeInTheDocument();
});
});
});

timelineIdsNoHoverActions.forEach((timelineId) => {
test(`it does NOT render hover actions when 'isDraggable' is false and timelineId is '${timelineId}'`, async () => {
const isDraggable = false;

const { container } = render(
<TestProviders>
<DragDropContextWrapper browserFields={mockBrowserFields}>
<DraggableWrapper
dataProvider={dataProvider}
isDraggable={isDraggable}
render={() => message}
timelineId={timelineId}
/>
</DragDropContextWrapper>
</TestProviders>
);

fireEvent.mouseEnter(container.querySelector('[data-test-subj="withHoverActionsButton"]')!);

await waitFor(() => {
expect(screen.queryByTestId('hover-actions-copy-button')).not.toBeInTheDocument();
});
});
});
});

describe('text truncation styling', () => {
Expand Down Expand Up @@ -192,4 +279,18 @@ describe('ConditionalPortal', () => {
expect(getStyle(style, snapshot)).toHaveProperty('transitionDuration', '0.00000001s');
});
});

describe('disableHoverActions', () => {
timelineIdsNoHoverActions.forEach((timelineId) =>
test(`it returns true when timelineId is ${timelineId}`, () => {
expect(disableHoverActions(timelineId)).toBe(true);
})
);

timelineIdsWithHoverActions.forEach((timelineId) =>
test(`it returns false when timelineId is ${timelineId}`, () => {
expect(disableHoverActions(timelineId)).toBe(false);
})
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { Draggable, Droppable } from 'react-beautiful-dnd';
import { useDispatch } from 'react-redux';
import styled from 'styled-components';

import { TimelineId } from '../../../../common/types';
import { dragAndDropActions } from '../../store/drag_and_drop';
import type { DataProvider } from '../../../timelines/components/timeline/data_providers/data_provider';
import { ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID } from '../../../timelines/components/row_renderers_browser/constants';
Expand Down Expand Up @@ -108,6 +109,9 @@ interface Props {
onFilterAdded?: () => void;
}

export const disableHoverActions = (timelineId: string | undefined): boolean =>
[TimelineId.rulePreview, ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID].includes(timelineId ?? '');

/**
* Wraps a draggable component to handle registration / unregistration of the
* data provider associated with the item being dropped
Expand Down Expand Up @@ -370,7 +374,7 @@ const DraggableWrapperComponent: React.FC<Props> = ({
<WithHoverActions
alwaysShow={showTopN || hoverActionsOwnFocus}
closePopOverTrigger={closePopOverTrigger}
hoverContent={hoverContent}
hoverContent={disableHoverActions(timelineId) ? undefined : hoverContent}
onCloseRequested={onCloseRequested}
render={renderContent}
/>
Expand Down

0 comments on commit f571f80

Please sign in to comment.