From 780c43513ad49c73515ec65a725df6a7a89eb445 Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Mon, 25 Oct 2021 11:16:13 +0100 Subject: [PATCH] [Security Solution] Fix tooltip for timeline sourcerer (#115950) * fix tooltip for event picker * remove unused mock * remove unused dependency Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/sourcerer/index.test.tsx | 34 ++++- .../common/components/sourcerer/index.tsx | 103 +++++++------ .../search_or_filter/pick_events.test.tsx | 55 ++++++- .../timeline/search_or_filter/pick_events.tsx | 137 ++++++++++-------- 4 files changed, 215 insertions(+), 114 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/sourcerer/index.test.tsx index 352fc9544782..591cba6b1938 100644 --- a/x-pack/plugins/security_solution/public/common/components/sourcerer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/index.test.tsx @@ -7,6 +7,9 @@ import React from 'react'; import { mount } from 'enzyme'; +import { waitFor } from '@testing-library/react'; + +import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; import { SourcererScopeName } from '../../store/sourcerer/model'; import { Sourcerer } from './index'; import { DEFAULT_INDEX_PATTERN } from '../../../../common/constants'; @@ -19,8 +22,6 @@ import { TestProviders, } from '../../mock'; import { createStore, State } from '../../store'; -import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; -import { waitFor } from '@testing-library/react'; const mockDispatch = jest.fn(); jest.mock('react-redux', () => { @@ -46,6 +47,7 @@ const mockOptions = [ const defaultProps = { scope: sourcererModel.SourcererScopeName.default, }; + describe('Sourcerer component', () => { beforeEach(() => { jest.clearAllMocks(); @@ -59,6 +61,34 @@ describe('Sourcerer component', () => { store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); }); + it('renders tooltip', () => { + const wrapper = mount( + + + + ); + expect(wrapper.find('[data-test-subj="sourcerer-tooltip"]').prop('content')).toEqual( + mockOptions + .map((p) => p.label) + .sort() + .join(', ') + ); + }); + + it('renders popover button inside tooltip', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper + .find('[data-test-subj="sourcerer-tooltip"] [data-test-subj="sourcerer-trigger"]') + .exists() + ).toBeTruthy(); + }); + // Using props callback instead of simulating clicks, // because EuiSelectable uses a virtualized list, which isn't easily testable via test subjects it('Mounts with all options selected', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/index.tsx b/x-pack/plugins/security_solution/public/common/components/sourcerer/index.tsx index 8d0bb6410651..3e922176f998 100644 --- a/x-pack/plugins/security_solution/public/common/components/sourcerer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/index.tsx @@ -169,55 +169,62 @@ export const Sourcerer = React.memo(({ scope: scopeId } [isPopoverOpen, sourcererScope.selectedPatterns] ); + const buttonWithTooptip = useMemo(() => { + return tooltipContent ? ( + + {trigger} + + ) : ( + trigger + ); + }, [trigger, tooltipContent]); + return ( - - - - - <>{i18n.SELECT_INDEX_PATTERNS} - - - {i18n.INDEX_PATTERNS_SELECTION_LABEL} - - {comboBox} - - - - - {i18n.INDEX_PATTERNS_RESET} - - - - - {i18n.SAVE_INDEX_PATTERNS} - - - - - - + + + + <>{i18n.SELECT_INDEX_PATTERNS} + + + {i18n.INDEX_PATTERNS_SELECTION_LABEL} + + {comboBox} + + + + + {i18n.INDEX_PATTERNS_RESET} + + + + + {i18n.SAVE_INDEX_PATTERNS} + + + + + ); }); Sourcerer.displayName = 'Sourcerer'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/pick_events.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/pick_events.test.tsx index 3c6dc68edefc..e519cfcd204a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/pick_events.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/pick_events.test.tsx @@ -5,7 +5,9 @@ * 2.0. */ -import { fireEvent, render } from '@testing-library/react'; +import { fireEvent, render, within } from '@testing-library/react'; +import { EuiToolTip } from '@elastic/eui'; + import React from 'react'; import { PickEventType } from './pick_events'; import { @@ -19,6 +21,14 @@ import { TimelineEventsType } from '../../../../../common'; import { createStore } from '../../../../common/store'; import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; +jest.mock('@elastic/eui', () => { + const actual = jest.requireActual('@elastic/eui'); + return { + ...actual, + EuiToolTip: jest.fn(), + }; +}); + describe('pick_events', () => { const defaultProps = { eventType: 'all' as TimelineEventsType, @@ -53,6 +63,23 @@ describe('pick_events', () => { }, }; const store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + + const mockTooltip = ({ + tooltipContent, + children, + }: { + tooltipContent: string; + children: React.ReactElement; + }) => ( +
+ {tooltipContent} + {children} +
+ ); + + beforeAll(() => { + (EuiToolTip as unknown as jest.Mock).mockImplementation(mockTooltip); + }); beforeEach(() => { jest.clearAllMocks(); jest.restoreAllMocks(); @@ -68,6 +95,32 @@ describe('pick_events', () => { initialPatterns.sort().join('') ); }); + + it('renders tooltip', () => { + render( + + + + ); + + expect((EuiToolTip as unknown as jest.Mock).mock.calls[0][0].content).toEqual( + initialPatterns + .filter((p) => p != null) + .sort() + .join(', ') + ); + }); + + it('renders popover button inside tooltip', () => { + const wrapper = render( + + + + ); + const tooltip = wrapper.getByTestId('timeline-sourcerer-tooltip'); + expect(within(tooltip).getByTestId('sourcerer-timeline-trigger')).toBeTruthy(); + }); + it('correctly filters options', () => { const wrapper = render( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/pick_events.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/pick_events.tsx index dbe04eccac52..6d86d7c0f133 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/pick_events.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/pick_events.tsx @@ -295,6 +295,20 @@ const PickEventTypeComponents: React.FC = ({ [isPopoverOpen, sourcererScope.selectedPatterns] ); + const buttonWithTooptip = useMemo(() => { + return tooltipContent ? ( + + {button} + + ) : ( + button + ); + }, [button, tooltipContent]); + const ButtonContent = useMemo( () => ( @@ -326,69 +340,66 @@ const PickEventTypeComponents: React.FC = ({ return ( - - - - - <>{i18n.SELECT_INDEX_PATTERNS} - - - {filter} - - - <> - - {comboBox} - - - {!showAdvanceSettings && ( - <> - - - {i18n.CONFIGURE_INDEX_PATTERNS} - - - )} - - - - - {i18n.DATA_SOURCES_RESET} - - - - - {i18n.SAVE_INDEX_PATTERNS} - - - - - - + + + + <>{i18n.SELECT_INDEX_PATTERNS} + + + {filter} + + + <> + + {comboBox} + + + {!showAdvanceSettings && ( + <> + + + {i18n.CONFIGURE_INDEX_PATTERNS} + + + )} + + + + + {i18n.DATA_SOURCES_RESET} + + + + + {i18n.SAVE_INDEX_PATTERNS} + + + + + ); };