diff --git a/x-pack/legacy/plugins/siem/public/components/flow_controls/flow_target_select.tsx b/x-pack/legacy/plugins/siem/public/components/flow_controls/flow_target_select.tsx index b1c463fdc32f40..f5dd4650159cd3 100644 --- a/x-pack/legacy/plugins/siem/public/components/flow_controls/flow_target_select.tsx +++ b/x-pack/legacy/plugins/siem/public/components/flow_controls/flow_target_select.tsx @@ -5,7 +5,7 @@ */ import { EuiSuperSelect } from '@elastic/eui'; -import React from 'react'; +import React, { useCallback } from 'react'; import { ActionCreator } from 'typescript-fsa'; import { FlowDirection, FlowTarget } from '../../graphql/types'; @@ -65,22 +65,27 @@ export const FlowTargetSelect = React.memo( selectedTarget, displayTextOverride = [], updateFlowTargetAction, - }) => ( - - option.directions.includes(selectedDirection) - ) - : toggleTargetOptions(id, displayTextOverride) - } - valueOfSelected={selectedTarget} - onChange={(newFlowTarget: FlowTarget) => - onChangeTarget(newFlowTarget, updateFlowTargetAction) - } - isLoading={isLoading} - /> - ) + }) => { + const handleChange = useCallback( + (newFlowTarget: FlowTarget) => onChangeTarget(newFlowTarget, updateFlowTargetAction), + [updateFlowTargetAction] + ); + + return ( + + option.directions.includes(selectedDirection) + ) + : toggleTargetOptions(id, displayTextOverride) + } + valueOfSelected={selectedTarget} + onChange={handleChange} + isLoading={isLoading} + /> + ); + } ); FlowTargetSelect.displayName = 'FlowTargetSelect'; diff --git a/x-pack/legacy/plugins/siem/public/components/inspect/index.tsx b/x-pack/legacy/plugins/siem/public/components/inspect/index.tsx index 6908aba542e4ca..04d6d94d6624d6 100644 --- a/x-pack/legacy/plugins/siem/public/components/inspect/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/inspect/index.tsx @@ -6,7 +6,7 @@ import { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui'; import { getOr } from 'lodash/fp'; -import React from 'react'; +import React, { useCallback } from 'react'; import { connect } from 'react-redux'; import { ActionCreator } from 'typescript-fsa'; import styled from 'styled-components'; @@ -72,72 +72,71 @@ const InspectButtonComponent = React.memo( setIsInspected, show, title = '', - }: InspectButtonProps) => ( - - {inputId === 'timeline' && !compact && ( - { - setIsInspected({ - id: queryId, - inputId, - isInspected: true, - selectedInspectIndex: inspectIndex, - }); - }} - > - {i18n.INSPECT} - - )} - {(inputId === 'global' || compact) && ( - { - setIsInspected({ - id: queryId, - inputId, - isInspected: true, - selectedInspectIndex: inspectIndex, - }); - }} - /> - )} - { - if (onCloseInspect != null) { - onCloseInspect(); + }: InspectButtonProps) => { + const handleClick = useCallback(() => { + setIsInspected({ + id: queryId, + inputId, + isInspected: true, + selectedInspectIndex: inspectIndex, + }); + }, [setIsInspected, queryId, inputId, inspectIndex]); + + const handleCloseModal = useCallback(() => { + if (onCloseInspect != null) { + onCloseInspect(); + } + setIsInspected({ + id: queryId, + inputId, + isInspected: false, + selectedInspectIndex: inspectIndex, + }); + }, [onCloseInspect, setIsInspected, queryId, inputId, inspectIndex]); + + return ( + + {inputId === 'timeline' && !compact && ( + + {i18n.INSPECT} + + )} + {(inputId === 'global' || compact) && ( + + )} + 0 ? inspect.dsl[inspectIndex] : null} + response={ + inspect != null && inspect.response.length > 0 ? inspect.response[inspectIndex] : null } - setIsInspected({ - id: queryId, - inputId, - isInspected: false, - selectedInspectIndex: inspectIndex, - }); - }} - isShowing={!loading && selectedInspectIndex === inspectIndex && isInspected} - request={inspect != null && inspect.dsl.length > 0 ? inspect.dsl[inspectIndex] : null} - response={ - inspect != null && inspect.response.length > 0 ? inspect.response[inspectIndex] : null - } - title={title} - data-test-subj="inspect-modal" - /> - - ) + title={title} + data-test-subj="inspect-modal" + /> + + ); + } ); InspectButtonComponent.displayName = 'InspectButtonComponent'; diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx index f79c61a29c26b4..e1ccfd79a89a0b 100644 --- a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import { ScaleType } from '@elastic/charts'; import darkTheme from '@elastic/eui/dist/eui_theme_dark.json'; @@ -54,14 +54,17 @@ export const MatrixHistogram = ({ if (totalCount >= 0 && loadingInitial) { setLoadingInitial(false); } - }, [loading]); + }, [loading, loadingInitial, totalCount]); + + const handleOnMouseEnter = useCallback(() => setShowInspect(true), []); + const handleOnMouseLeave = useCallback(() => setShowInspect(false), []); return ( setShowInspect(true)} - onMouseLeave={() => setShowInspect(false)} + onMouseEnter={handleOnMouseEnter} + onMouseLeave={handleOnMouseLeave} > ( - - snapshot.isDragging ? ( - - - - ) : ( - <> - {index !== 0 && ( - <> - {','} - - - )} - {getScoreString(score.severity)} - - ) - } - /> -); +}): JSX.Element => { + const scoreString = getScoreString(score.severity); + + return ( + + snapshot.isDragging ? ( + + + + ) : ( + <> + {index !== 0 && ( + <> + {','} + + + )} + {scoreString} + + ) + } + /> + ); +}; DraggableScoreComponent.displayName = 'DraggableScoreComponent'; diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/filters/jobs_table_filters.tsx b/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/filters/jobs_table_filters.tsx index 74e61f27fb2d19..551ed5f08bd76e 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/filters/jobs_table_filters.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/filters/jobs_table_filters.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Dispatch, SetStateAction, useEffect, useState } from 'react'; +import React, { Dispatch, SetStateAction, useEffect, useState, useCallback } from 'react'; import { EuiFilterButton, @@ -40,7 +40,22 @@ export const JobsTableFiltersComponent = ({ siemJobs, onFilterChanged }: JobsTab // Propagate filter changes to parent useEffect(() => { onFilterChanged({ filterQuery, showCustomJobs, showElasticJobs, selectedGroups }); - }, [filterQuery, selectedGroups.sort().join(), showCustomJobs, showElasticJobs]); + }, [filterQuery, selectedGroups, showCustomJobs, showElasticJobs, onFilterChanged]); + + const handleChange = useCallback( + (query: EuiSearchBarQuery) => setFilterQuery(query.queryText.trim()), + [setFilterQuery] + ); + + const handleElasticJobsClick = useCallback(() => { + setShowElasticJobs(!showElasticJobs); + setShowCustomJobs(false); + }, [setShowElasticJobs, showElasticJobs, setShowCustomJobs]); + + const handleCustomJobsClick = useCallback(() => { + setShowCustomJobs(!showCustomJobs); + setShowElasticJobs(false); + }, [setShowElasticJobs, showCustomJobs, setShowCustomJobs]); return ( @@ -51,7 +66,7 @@ export const JobsTableFiltersComponent = ({ siemJobs, onFilterChanged }: JobsTab placeholder: i18n.FILTER_PLACEHOLDER, incremental: true, }} - onChange={(query: EuiSearchBarQuery) => setFilterQuery(query.queryText.trim())} + onChange={handleChange} /> @@ -65,10 +80,7 @@ export const JobsTableFiltersComponent = ({ siemJobs, onFilterChanged }: JobsTab { - setShowElasticJobs(!showElasticJobs); - setShowCustomJobs(false); - }} + onClick={handleElasticJobsClick} data-test-subj="show-elastic-jobs-filter-button" withNext > @@ -76,10 +88,7 @@ export const JobsTableFiltersComponent = ({ siemJobs, onFilterChanged }: JobsTab { - setShowCustomJobs(!showCustomJobs); - setShowElasticJobs(false); - }} + onClick={handleCustomJobsClick} data-test-subj="show-custom-jobs-filter-button" > {i18n.SHOW_CUSTOM_JOBS} diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/job_switch.tsx b/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/job_switch.tsx index c915bf04126ec4..0d503e2db3d9d1 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/job_switch.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/job_switch.tsx @@ -5,7 +5,7 @@ */ import styled from 'styled-components'; -import React, { useState } from 'react'; +import React, { useState, useCallback } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiSwitch } from '@elastic/eui'; import { SiemJob } from '../types'; @@ -47,6 +47,13 @@ export const JobSwitchComponent = ({ onJobStateChange, }: JobSwitchProps) => { const [isLoading, setIsLoading] = useState(false); + const handleChange = useCallback( + e => { + setIsLoading(true); + onJobStateChange(job, job.latestTimestampMs || 0, e.target.checked); + }, + [job, setIsLoading, onJobStateChange] + ); return ( @@ -58,10 +65,7 @@ export const JobSwitchComponent = ({ data-test-subj="job-switch" disabled={isFailure(job.jobState, job.datafeedState)} checked={isChecked(job.jobState, job.datafeedState)} - onChange={e => { - setIsLoading(true); - onJobStateChange(job, job.latestTimestampMs || 0, e.target.checked); - }} + onChange={handleChange} showLabel={false} label="" /> diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.test.tsx index 00b1d4c066d4a2..840e2bf3f42dcd 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.test.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { mount, shallow } from 'enzyme'; +import { mount } from 'enzyme'; import * as React from 'react'; import { navTabs } from '../../../pages/home/home_navigations'; @@ -62,13 +62,13 @@ describe('Tab Navigation', () => { }, }; test('it mounts with correct tab highlighted', () => { - const wrapper = shallow(); - const hostsTab = wrapper.find('[data-test-subj="navigation-hosts"]'); + const wrapper = mount(); + const hostsTab = wrapper.find('EuiTab[data-test-subj="navigation-hosts"]'); expect(hostsTab.prop('isSelected')).toBeTruthy(); }); test('it changes active tab when nav changes by props', () => { const wrapper = mount(); - const networkTab = () => wrapper.find('[data-test-subj="navigation-network"]').first(); + const networkTab = () => wrapper.find('EuiTab[data-test-subj="navigation-network"]').first(); expect(networkTab().prop('isSelected')).toBeFalsy(); wrapper.setProps({ pageName: 'network', @@ -79,8 +79,8 @@ describe('Tab Navigation', () => { expect(networkTab().prop('isSelected')).toBeTruthy(); }); test('it carries the url state in the link', () => { - const wrapper = shallow(); - const firstTab = wrapper.find('[data-test-subj="navigation-network"]'); + const wrapper = mount(); + const firstTab = wrapper.find('EuiTab[data-test-subj="navigation-network"]'); expect(firstTab.props().href).toBe( "#/link-to/network?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))" ); @@ -126,9 +126,9 @@ describe('Tab Navigation', () => { }, }; test('it mounts with correct tab highlighted', () => { - const wrapper = shallow(); + const wrapper = mount(); const tableNavigationTab = wrapper.find( - `[data-test-subj="navigation-${HostsTableType.authentications}"]` + `EuiTab[data-test-subj="navigation-${HostsTableType.authentications}"]` ); expect(tableNavigationTab.prop('isSelected')).toBeTruthy(); @@ -147,9 +147,9 @@ describe('Tab Navigation', () => { expect(tableNavigationTab().prop('isSelected')).toBeTruthy(); }); test('it carries the url state in the link', () => { - const wrapper = shallow(); + const wrapper = mount(); const firstTab = wrapper.find( - `[data-test-subj="navigation-${HostsTableType.authentications}"]` + `EuiTab[data-test-subj="navigation-${HostsTableType.authentications}"]` ); expect(firstTab.props().href).toBe( `#/${pageName}/${hostName}/${HostsTableType.authentications}?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))` diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx index d405ec404b1112..b653624ec1f67f 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx @@ -5,23 +5,52 @@ */ import { EuiTab, EuiTabs } from '@elastic/eui'; import { getOr } from 'lodash/fp'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useCallback, useMemo } from 'react'; import { trackUiAction as track, METRIC_TYPE, TELEMETRY_EVENT } from '../../../lib/track_usage'; import { getSearch } from '../helpers'; -import { TabNavigationProps } from './types'; +import { TabNavigationProps, TabNavigationItemProps } from './types'; + +const TabNavigationItemComponent = ({ + href, + hrefWithSearch, + id, + disabled, + name, + isSelected, +}: TabNavigationItemProps) => { + const handleClick = useCallback(() => { + track(METRIC_TYPE.CLICK, `${TELEMETRY_EVENT.TAB_CLICKED}${id}`); + }, [id]); + + return ( + + {name} + + ); +}; + +const TabNavigationItem = React.memo(TabNavigationItemComponent); export const TabNavigationComponent = (props: TabNavigationProps) => { const { display, navTabs, pageName, tabName } = props; - const mapLocationToTab = (): string => { - return getOr( - '', - 'id', - Object.values(navTabs).find(item => tabName === item.id || pageName === item.id) - ); - }; - + const mapLocationToTab = useCallback( + (): string => + getOr( + '', + 'id', + Object.values(navTabs).find(item => tabName === item.id || pageName === item.id) + ), + [pageName, tabName, navTabs] + ); const [selectedTabId, setSelectedTabId] = useState(mapLocationToTab()); useEffect(() => { const currentTabSelected = mapLocationToTab(); @@ -31,26 +60,30 @@ export const TabNavigationComponent = (props: TabNavigationProps) => { } // we do need navTabs in case the selectedTabId appears after initial load (ex. checking permissions for anomalies) - }, [pageName, tabName, navTabs]); - - const renderTabs = (): JSX.Element[] => - Object.values(navTabs).map(tab => ( - { - track(METRIC_TYPE.CLICK, `${TELEMETRY_EVENT.TAB_CLICKED}${tab.id}`); - }} - > - {tab.name} - - )); - - return {renderTabs()}; + }, [pageName, tabName, navTabs, mapLocationToTab, selectedTabId]); + + const renderTabs = useMemo( + () => + Object.values(navTabs).map(tab => { + const isSelected = selectedTabId === tab.id; + const hrefWithSearch = tab.href + getSearch(tab, props); + + return ( + + ); + }), + [navTabs, selectedTabId, props] + ); + + return {renderTabs}; }; TabNavigationComponent.displayName = 'TabNavigationComponent'; diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts index 1283691e658065..3fac783b550479 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/types.ts @@ -22,3 +22,12 @@ export interface TabNavigationProps extends SiemNavigationProps { [CONSTANTS.timerange]: UrlInputsModel; [CONSTANTS.timeline]: Timeline; } + +export interface TabNavigationItemProps { + href: string; + hrefWithSearch: string; + id: string; + disabled: boolean; + name: string; + isSelected: boolean; +} diff --git a/x-pack/legacy/plugins/siem/public/components/notes/add_note/index.tsx b/x-pack/legacy/plugins/siem/public/components/notes/add_note/index.tsx index 6e13538cf9beab..28cab2b46755f8 100644 --- a/x-pack/legacy/plugins/siem/public/components/notes/add_note/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/notes/add_note/index.tsx @@ -5,7 +5,7 @@ */ import { EuiButton, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import * as React from 'react'; +import React, { useCallback } from 'react'; import styled from 'styled-components'; import { MarkdownHint } from '../../markdown/markdown_hint'; @@ -49,38 +49,44 @@ export const AddNote = React.memo<{ onCancelAddNote?: () => void; updateNewNote: UpdateInternalNewNote; updateNote: UpdateNote; -}>(({ associateNote, getNewNoteId, newNote, onCancelAddNote, updateNewNote, updateNote }) => ( - - - - 0} /> - - - {onCancelAddNote != null ? ( +}>(({ associateNote, getNewNoteId, newNote, onCancelAddNote, updateNewNote, updateNote }) => { + const handleClick = useCallback( + () => + updateAndAssociateNode({ + associateNote, + getNewNoteId, + newNote, + updateNewNote, + updateNote, + }), + [associateNote, getNewNoteId, newNote, updateNewNote, updateNote] + ); + + return ( + + + + 0} /> + + + {onCancelAddNote != null ? ( + + + + ) : null} - + + {i18n.ADD_NOTE} + - ) : null} - - - updateAndAssociateNode({ - associateNote, - getNewNoteId, - newNote, - updateNewNote, - updateNote, - }) - } - > - {i18n.ADD_NOTE} - - - - -)); + + + ); +}); AddNote.displayName = 'AddNote'; diff --git a/x-pack/legacy/plugins/siem/public/components/page/hosts/authentications_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/hosts/authentications_table/index.tsx index 7efcde5b89c675..ad5755068e6625 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/hosts/authentications_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/hosts/authentications_table/index.tsx @@ -5,7 +5,7 @@ */ import { has } from 'lodash/fp'; -import React from 'react'; +import React, { useCallback } from 'react'; import { connect } from 'react-redux'; import { ActionCreator } from 'typescript-fsa'; @@ -98,39 +98,49 @@ const AuthenticationTableComponent = React.memo( type, updateTableActivePage, updateTableLimit, - }) => ( - loadPage(newActivePage)} - pageOfItems={data} - showMorePagesIndicator={showMorePagesIndicator} - totalCount={fakeTotalCount} - updateLimitPagination={newLimit => + }) => { + const updateLimitPagination = useCallback( + newLimit => updateTableLimit({ hostsType: type, limit: newLimit, tableType, - }) - } - updateActivePage={newPage => + }), + [type, updateTableLimit] + ); + + const updateActivePage = useCallback( + newPage => updateTableActivePage({ activePage: newPage, hostsType: type, tableType, - }) - } - /> - ) + }), + [type, updateTableActivePage] + ); + + return ( + + ); + } ); AuthenticationTableComponent.displayName = 'AuthenticationTableComponent'; diff --git a/x-pack/legacy/plugins/siem/public/components/page/hosts/host_overview/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/hosts/host_overview/index.tsx index ba953d8e74b29d..437d14edeb5c8d 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/hosts/host_overview/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/hosts/host_overview/index.tsx @@ -8,7 +8,7 @@ import { EuiFlexItem } from '@elastic/eui'; import darkTheme from '@elastic/eui/dist/eui_theme_dark.json'; import lightTheme from '@elastic/eui/dist/eui_theme_light.json'; import { getOr } from 'lodash/fp'; -import React, { useContext, useState } from 'react'; +import React, { useContext, useState, useCallback } from 'react'; import { DEFAULT_DARK_MODE } from '../../../../../common/constants'; import { DescriptionList } from '../../../../../common/utility_types'; @@ -165,11 +165,11 @@ export const HostOverview = React.memo( ], ]; + const handleOnMouseEnter = useCallback(() => setShowInspect(true), []); + const handleOnMouseLeave = useCallback(() => setShowInspect(false), []); + return ( - setShowInspect(true)} - onMouseLeave={() => setShowInspect(false)} - > + ( updateTableActivePage, updateTableLimit, }) => { + const updateLimitPagination = useCallback( + newLimit => + updateTableLimit({ + hostsType: type, + limit: newLimit, + tableType, + }), + [type, updateTableLimit] + ); + + const updateActivePage = useCallback( + newPage => + updateTableActivePage({ + activePage: newPage, + hostsType: type, + tableType, + }), + [type, updateTableActivePage] + ); + const onChange = useCallback( (criteria: Criteria) => { if (criteria.sort != null) { @@ -130,7 +150,7 @@ const HostsTableComponent = React.memo( } } }, - [direction, sortField, type] + [direction, sortField, type, updateHostsSort] ); const hostsColumns = useMemo(() => getHostsColumns(), []); @@ -153,26 +173,14 @@ const HostsTableComponent = React.memo( itemsPerRow={rowItems} limit={limit} loading={loading} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} onChange={onChange} pageOfItems={data} showMorePagesIndicator={showMorePagesIndicator} sorting={sorting} totalCount={fakeTotalCount} - updateLimitPagination={newLimit => - updateTableLimit({ - hostsType: type, - limit: newLimit, - tableType, - }) - } - updateActivePage={newPage => - updateTableActivePage({ - activePage: newPage, - hostsType: type, - tableType, - }) - } + updateLimitPagination={updateLimitPagination} + updateActivePage={updateActivePage} /> ); } diff --git a/x-pack/legacy/plugins/siem/public/components/page/hosts/uncommon_process_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/hosts/uncommon_process_table/index.tsx index a4fd4d636b35e9..a8b9b2a1f61cea 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/hosts/uncommon_process_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/hosts/uncommon_process_table/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useCallback } from 'react'; import { connect } from 'react-redux'; import { ActionCreator } from 'typescript-fsa'; @@ -96,39 +96,49 @@ const UncommonProcessTableComponent = React.memo( updateTableActivePage, updateTableLimit, type, - }) => ( - loadPage(newActivePage)} - pageOfItems={data} - showMorePagesIndicator={showMorePagesIndicator} - totalCount={fakeTotalCount} - updateLimitPagination={newLimit => + }) => { + const updateLimitPagination = useCallback( + newLimit => updateTableLimit({ hostsType: type, limit: newLimit, tableType, - }) - } - updateActivePage={newPage => + }), + [type, updateTableLimit] + ); + + const updateActivePage = useCallback( + newPage => updateTableActivePage({ activePage: newPage, hostsType: type, tableType, - }) - } - /> - ) + }), + [type, updateTableActivePage] + ); + + return ( + + ); + } ); UncommonProcessTableComponent.displayName = 'UncommonProcessTableComponent'; diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/ip_overview/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/ip_overview/index.tsx index 70b4d033624389..8cb55f0d0fb589 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/ip_overview/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/network/ip_overview/index.tsx @@ -7,7 +7,7 @@ import { EuiFlexItem } from '@elastic/eui'; import darkTheme from '@elastic/eui/dist/eui_theme_dark.json'; import lightTheme from '@elastic/eui/dist/eui_theme_light.json'; -import React, { useContext, useState } from 'react'; +import React, { useContext, useState, useCallback } from 'react'; import { DEFAULT_DARK_MODE } from '../../../../../common/constants'; import { DescriptionList } from '../../../../../common/utility_types'; @@ -139,11 +139,12 @@ export const IpOverview = React.memo( { title: i18n.REPUTATION, description: reputationRenderer(ip) }, ], ]; + + const handleOnMouseEnter = useCallback(() => setShowInspect(true), []); + const handleOnMouseLeave = useCallback(() => setShowInspect(false), []); + return ( - setShowInspect(true)} - onMouseLeave={() => setShowInspect(false)} - > + ( type, updateNetworkTable, }) => { + const updateLimitPagination = useCallback( + newLimit => + updateNetworkTable({ + networkType: type, + tableType, + updates: { limit: newLimit }, + }), + [type, updateNetworkTable] + ); + + const updateActivePage = useCallback( + newPage => + updateNetworkTable({ + networkType: type, + tableType, + updates: { activePage: newPage }, + }), + [type, updateNetworkTable] + ); + const onChange = useCallback( (criteria: Criteria) => { if (criteria.sort != null) { @@ -93,7 +113,7 @@ export const NetworkDnsTableComponent = React.memo( } } }, - [sort, type] + [sort, type, updateNetworkTable] ); const onChangePtrIncluded = useCallback( @@ -103,7 +123,7 @@ export const NetworkDnsTableComponent = React.memo( tableType, updates: { isPtrIncluded: !isPtrIncluded }, }), - [type, isPtrIncluded] + [type, updateNetworkTable, isPtrIncluded] ); return ( @@ -123,7 +143,7 @@ export const NetworkDnsTableComponent = React.memo( isInspect={isInspect} limit={limit} loading={loading} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} onChange={onChange} pageOfItems={data} showMorePagesIndicator={showMorePagesIndicator} @@ -132,20 +152,8 @@ export const NetworkDnsTableComponent = React.memo( direction: sort.direction, }} totalCount={fakeTotalCount} - updateActivePage={newPage => - updateNetworkTable({ - networkType: type, - tableType, - updates: { activePage: newPage }, - }) - } - updateLimitPagination={newLimit => - updateNetworkTable({ - networkType: type, - tableType, - updates: { limit: newLimit }, - }) - } + updateActivePage={updateActivePage} + updateLimitPagination={updateLimitPagination} /> ); } diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.tsx index e5ad39b814caac..d25d94a06b8181 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useCallback } from 'react'; import { connect } from 'react-redux'; import { compose } from 'redux'; import { ActionCreator } from 'typescript-fsa'; @@ -76,23 +76,50 @@ const NetworkHttpTableComponent = React.memo( type, updateNetworkTable, }) => { - const onChange = (criteria: Criteria, tableType: networkModel.HttpTableType) => { - if (criteria.sort != null && criteria.sort.direction !== sort.direction) { - updateNetworkTable({ - networkType: type, - tableType, - updates: { - sort: { - direction: criteria.sort.direction, - }, - }, - }); - } - }; const tableType = type === networkModel.NetworkType.page ? networkModel.NetworkTableType.http : networkModel.IpDetailsTableType.http; + + const updateLimitPagination = useCallback( + newLimit => + updateNetworkTable({ + networkType: type, + tableType, + updates: { limit: newLimit }, + }), + [type, updateNetworkTable, tableType] + ); + + const updateActivePage = useCallback( + newPage => + updateNetworkTable({ + networkType: type, + tableType, + updates: { activePage: newPage }, + }), + [type, updateNetworkTable, tableType] + ); + + const onChange = useCallback( + (criteria: Criteria) => { + if (criteria.sort != null && criteria.sort.direction !== sort.direction) { + updateNetworkTable({ + networkType: type, + tableType, + updates: { + sort: { + direction: criteria.sort.direction, + }, + }, + }); + } + }, + [tableType, sort.direction, type, updateNetworkTable] + ); + + const sorting = { field: `node.${NetworkHttpFields.requestCount}`, direction: sort.direction }; + return ( ( isInspect={isInspect} limit={limit} loading={loading} - loadPage={newActivePage => loadPage(newActivePage)} - onChange={criteria => onChange(criteria, tableType)} + loadPage={loadPage} + onChange={onChange} pageOfItems={data} showMorePagesIndicator={showMorePagesIndicator} - sorting={{ field: `node.${NetworkHttpFields.requestCount}`, direction: sort.direction }} + sorting={sorting} totalCount={fakeTotalCount} - updateActivePage={newPage => - updateNetworkTable({ - networkType: type, - tableType, - updates: { activePage: newPage }, - }) - } - updateLimitPagination={newLimit => - updateNetworkTable({ - networkType: type, - tableType, - updates: { limit: newLimit }, - }) - } + updateActivePage={updateActivePage} + updateLimitPagination={updateLimitPagination} /> ); } diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_top_countries_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/network_top_countries_table/index.tsx index 81a48ca162e156..8fd5cc9b3f3c08 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/network_top_countries_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_top_countries_table/index.tsx @@ -5,7 +5,7 @@ */ import { isEqual, last } from 'lodash/fp'; -import React from 'react'; +import React, { useCallback, useMemo } from 'react'; import { connect } from 'react-redux'; import { compose } from 'redux'; import { ActionCreator } from 'typescript-fsa'; @@ -88,27 +88,6 @@ const NetworkTopCountriesTableComponent = React.memo { - const onChange = (criteria: Criteria, tableType: networkModel.TopCountriesTableType) => { - if (criteria.sort != null) { - const splitField = criteria.sort.field.split('.'); - const field = last(splitField); - const newSortDirection = field !== sort.field ? Direction.desc : criteria.sort.direction; // sort by desc on init click - const newTopCountriesSort: NetworkTopTablesSortField = { - field: field as NetworkTopTablesFields, - direction: newSortDirection, - }; - if (!isEqual(newTopCountriesSort, sort)) { - updateNetworkTable({ - networkType: type, - tableType, - updates: { - sort: newTopCountriesSort, - }, - }); - } - } - }; - let tableType: networkModel.TopCountriesTableType; const headerTitle: string = flowTargeted === FlowTargetSourceDest.source @@ -133,15 +112,61 @@ const NetworkTopCountriesTableComponent = React.memo + updateNetworkTable({ + networkType: type, + tableType, + updates: { limit: newLimit }, + }), + [type, updateNetworkTable, tableType] + ); + + const updateActivePage = useCallback( + newPage => + updateNetworkTable({ + networkType: type, + tableType, + updates: { activePage: newPage }, + }), + [type, updateNetworkTable, tableType] + ); + + const onChange = useCallback( + (criteria: Criteria) => { + if (criteria.sort != null) { + const splitField = criteria.sort.field.split('.'); + const lastField = last(splitField); + const newSortDirection = + lastField !== sort.field ? Direction.desc : criteria.sort.direction; // sort by desc on init click + const newTopCountriesSort: NetworkTopTablesSortField = { + field: lastField as NetworkTopTablesFields, + direction: newSortDirection, + }; + if (!isEqual(newTopCountriesSort, sort)) { + updateNetworkTable({ + networkType: type, + tableType, + updates: { + sort: newTopCountriesSort, + }, + }); + } + } + }, + [type, sort, tableType, updateNetworkTable] + ); + + const columns = useMemo( + () => + getCountriesColumnsCurated(indexPattern, flowTargeted, type, NetworkTopCountriesTableId), + [indexPattern, flowTargeted, type] + ); + return ( loadPage(newActivePage)} - onChange={criteria => onChange(criteria, tableType)} + loadPage={loadPage} + onChange={onChange} pageOfItems={data} showMorePagesIndicator={showMorePagesIndicator} sorting={{ field, direction: sort.direction }} totalCount={fakeTotalCount} - updateActivePage={newPage => - updateNetworkTable({ - networkType: type, - tableType, - updates: { activePage: newPage }, - }) - } - updateLimitPagination={newLimit => - updateNetworkTable({ - networkType: type, - tableType, - updates: { limit: newLimit }, - }) - } + updateActivePage={updateActivePage} + updateLimitPagination={updateLimitPagination} /> ); } diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/index.tsx index 12b87a517b4f77..c38d8094581633 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { isEqual, last } from 'lodash/fp'; -import React, { useCallback } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { connect } from 'react-redux'; import { compose } from 'redux'; import { ActionCreator } from 'typescript-fsa'; @@ -87,8 +87,29 @@ const NetworkTopNFlowTableComponent = React.memo( type, updateNetworkTable, }) => { + const columns = useMemo( + () => getNFlowColumnsCurated(indexPattern, flowTargeted, type, NetworkTopNFlowTableId), + [indexPattern, flowTargeted, type] + ); + + let tableType: networkModel.TopNTableType; + const headerTitle: string = + flowTargeted === FlowTargetSourceDest.source ? i18n.SOURCE_IP : i18n.DESTINATION_IP; + + if (type === networkModel.NetworkType.page) { + tableType = + flowTargeted === FlowTargetSourceDest.source + ? networkModel.NetworkTableType.topNFlowSource + : networkModel.NetworkTableType.topNFlowDestination; + } else { + tableType = + flowTargeted === FlowTargetSourceDest.source + ? networkModel.IpDetailsTableType.topNFlowSource + : networkModel.IpDetailsTableType.topNFlowDestination; + } + const onChange = useCallback( - (criteria: Criteria, tableType: networkModel.TopNTableType) => { + (criteria: Criteria) => { if (criteria.sort != null) { const splitField = criteria.sort.field.split('.'); const field = last(splitField); @@ -108,35 +129,35 @@ const NetworkTopNFlowTableComponent = React.memo( } } }, - [sort, type] + [sort, type, tableType, updateNetworkTable] ); - let tableType: networkModel.TopNTableType; - const headerTitle: string = - flowTargeted === FlowTargetSourceDest.source ? i18n.SOURCE_IP : i18n.DESTINATION_IP; - - if (type === networkModel.NetworkType.page) { - tableType = - flowTargeted === FlowTargetSourceDest.source - ? networkModel.NetworkTableType.topNFlowSource - : networkModel.NetworkTableType.topNFlowDestination; - } else { - tableType = - flowTargeted === FlowTargetSourceDest.source - ? networkModel.IpDetailsTableType.topNFlowSource - : networkModel.IpDetailsTableType.topNFlowDestination; - } - const field = sort.field === NetworkTopTablesFields.bytes_out || sort.field === NetworkTopTablesFields.bytes_in ? `node.network.${sort.field}` : `node.${flowTargeted}.${sort.field}`; + const updateActivePage = useCallback( + newPage => + updateNetworkTable({ + networkType: type, + tableType, + updates: { activePage: newPage }, + }), + [updateNetworkTable, type, tableType] + ); + + const updateLimitPagination = useCallback( + newLimit => + updateNetworkTable({ networkType: type, tableType, updates: { limit: newLimit } }), + [updateNetworkTable, type, tableType] + ); + return ( ( itemsPerRow={rowItems} limit={limit} loading={loading} - loadPage={newActivePage => loadPage(newActivePage)} - onChange={criteria => onChange(criteria, tableType)} + loadPage={loadPage} + onChange={onChange} pageOfItems={data} showMorePagesIndicator={showMorePagesIndicator} sorting={{ field, direction: sort.direction }} totalCount={fakeTotalCount} - updateActivePage={newPage => - updateNetworkTable({ - networkType: type, - tableType, - updates: { activePage: newPage }, - }) - } - updateLimitPagination={newLimit => - updateNetworkTable({ networkType: type, tableType, updates: { limit: newLimit } }) - } + updateActivePage={updateActivePage} + updateLimitPagination={updateLimitPagination} /> ); } diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/tls_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/tls_table/index.tsx index 7dd9ca0273c5b8..026ab9537d3d74 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/tls_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/network/tls_table/index.tsx @@ -79,6 +79,26 @@ const TlsTableComponent = React.memo( ? networkModel.NetworkTableType.tls : networkModel.IpDetailsTableType.tls; + const updateLimitPagination = useCallback( + newLimit => + updateNetworkTable({ + networkType: type, + tableType, + updates: { limit: newLimit }, + }), + [type, updateNetworkTable, tableType] + ); + + const updateActivePage = useCallback( + newPage => + updateNetworkTable({ + networkType: type, + tableType, + updates: { activePage: newPage }, + }), + [type, updateNetworkTable, tableType] + ); + const onChange = useCallback( (criteria: Criteria) => { if (criteria.sort != null) { @@ -96,8 +116,9 @@ const TlsTableComponent = React.memo( } } }, - [sort, type] + [sort, type, tableType, updateNetworkTable] ); + return ( ( itemsPerRow={rowItems} limit={limit} loading={loading} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} onChange={onChange} pageOfItems={data} sorting={getSortField(sort)} totalCount={fakeTotalCount} - updateActivePage={newPage => - updateNetworkTable({ - networkType: type, - tableType, - updates: { activePage: newPage }, - }) - } - updateLimitPagination={newLimit => - updateNetworkTable({ - networkType: type, - tableType, - updates: { limit: newLimit }, - }) - } + updateActivePage={updateActivePage} + updateLimitPagination={updateLimitPagination} /> ); } diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/users_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/users_table/index.tsx index 8da41fca8f3841..14fbf4860f7c03 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/users_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/network/users_table/index.tsx @@ -78,6 +78,26 @@ const UsersTableComponent = React.memo( updateNetworkTable, sort, }) => { + const updateLimitPagination = useCallback( + newLimit => + updateNetworkTable({ + networkType: type, + tableType, + updates: { limit: newLimit }, + }), + [type, updateNetworkTable] + ); + + const updateActivePage = useCallback( + newPage => + updateNetworkTable({ + networkType: type, + tableType, + updates: { activePage: newPage }, + }), + [type, updateNetworkTable] + ); + const onChange = useCallback( (criteria: Criteria) => { if (criteria.sort != null) { @@ -95,7 +115,7 @@ const UsersTableComponent = React.memo( } } }, - [sort, type] + [sort, type, updateNetworkTable] ); return ( @@ -112,25 +132,13 @@ const UsersTableComponent = React.memo( itemsPerRow={rowItems} limit={limit} loading={loading} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} onChange={onChange} pageOfItems={data} sorting={getSortField(sort)} totalCount={fakeTotalCount} - updateActivePage={newPage => - updateNetworkTable({ - networkType: type, - tableType, - updates: { activePage: newPage }, - }) - } - updateLimitPagination={newLimit => - updateNetworkTable({ - networkType: type, - tableType, - updates: { limit: newLimit }, - }) - } + updateActivePage={updateActivePage} + updateLimitPagination={updateLimitPagination} /> ); } diff --git a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx index 077a2d9aebe527..302917c3de93e3 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx @@ -6,7 +6,7 @@ import { EuiButton, EuiFlexItem, EuiPanel } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import React, { useState } from 'react'; +import React, { useState, useCallback } from 'react'; import { HeaderSection } from '../../../header_section'; import { manageQuery } from '../../../page/manage_query'; @@ -38,9 +38,12 @@ const OverviewHostStatsManage = manageQuery(OverviewHostStats); type OverviewHostProps = OwnProps; export const OverviewHost = React.memo(({ endDate, startDate, setQuery }) => { const [isHover, setIsHover] = useState(false); + const handleMouseEnter = useCallback(() => setIsHover(true), [setIsHover]); + const handleMouseLeave = useCallback(() => setIsHover(false), [setIsHover]); + return ( - setIsHover(true)} onMouseLeave={() => setIsHover(false)}> + (({ endDate, startDate, setQuery }) => { const [isHover, setIsHover] = useState(false); + const handleMouseEnter = useCallback(() => setIsHover(true), [setIsHover]); + const handleMouseLeave = useCallback(() => setIsHover(false), [setIsHover]); + return ( - setIsHover(true)} onMouseLeave={() => setIsHover(false)}> + diff --git a/x-pack/legacy/plugins/siem/public/components/paginated_table/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/paginated_table/index.test.tsx index 91ae271d1ccac5..0444360d2b965a 100644 --- a/x-pack/legacy/plugins/siem/public/components/paginated_table/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/paginated_table/index.test.tsx @@ -48,11 +48,11 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={1} loading={false} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={10} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -75,11 +75,11 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={1} loading={true} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={[]} showMorePagesIndicator={true} totalCount={10} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -104,11 +104,11 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={1} loading={true} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={10} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -131,7 +131,7 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={1} loading={false} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={10} @@ -168,11 +168,11 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={2} loading={false} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={10} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -199,11 +199,11 @@ describe('Paginated Table Component', () => { itemsPerRow={[]} limit={2} loading={false} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={10} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -233,7 +233,7 @@ describe('Paginated Table Component', () => { showMorePagesIndicator={true} sorting={{ direction: Direction.asc, field: 'node.host.name' }} totalCount={10} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -256,11 +256,11 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={DEFAULT_MAX_TABLE_QUERY_SIZE} loading={false} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={DEFAULT_MAX_TABLE_QUERY_SIZE * 3} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -286,11 +286,11 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={DEFAULT_MAX_TABLE_QUERY_SIZE} loading={false} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={30} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -312,11 +312,11 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={DEFAULT_MAX_TABLE_QUERY_SIZE} loading={false} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={1} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -340,11 +340,11 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={1} loading={false} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={10} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -370,11 +370,11 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={2} loading={false} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={10} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -408,11 +408,11 @@ describe('Paginated Table Component', () => { itemsPerRow: rowItems, limit: 1, loading: false, - loadPage: newActivePage => loadPage(newActivePage), + loadPage, pageOfItems: mockData.Hosts.edges, showMorePagesIndicator: true, totalCount: 10, - updateActivePage: activePage => updateActivePage(activePage), + updateActivePage, updateLimitPagination: limit => updateLimitPagination({ limit }), }; @@ -460,11 +460,11 @@ describe('Paginated Table Component', () => { itemsPerRow={rowItems} limit={2} loading={false} - loadPage={newActivePage => loadPage(newActivePage)} + loadPage={loadPage} pageOfItems={mockData.Hosts.edges} showMorePagesIndicator={true} totalCount={10} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> @@ -503,7 +503,7 @@ describe('Paginated Table Component', () => { showMorePagesIndicator={true} sorting={{ direction: Direction.asc, field: 'node.host.name' }} totalCount={10} - updateActivePage={activePage => updateActivePage(activePage)} + updateActivePage={updateActivePage} updateLimitPagination={limit => updateLimitPagination({ limit })} /> diff --git a/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx index aedec1a340bfde..7a7a8ee5dfa7d2 100644 --- a/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx @@ -16,7 +16,7 @@ import { EuiPopover, } from '@elastic/eui'; import { noop } from 'lodash/fp'; -import React, { memo, useState, useEffect } from 'react'; +import React, { memo, useState, useEffect, useCallback } from 'react'; import styled from 'styled-components'; import { Direction } from '../../graphql/types'; @@ -226,13 +226,15 @@ export const PaginatedTable = memo( )); const PaginationWrapper = showMorePagesIndicator ? PaginationEuiFlexItem : EuiFlexItem; + const handleOnMouseEnter = useCallback(() => setShowInspect(true), []); + const handleOnMouseLeave = useCallback(() => setShowInspect(false), []); return ( setShowInspect(true)} - onMouseLeave={() => setShowInspect(false)} + onMouseEnter={handleOnMouseEnter} + onMouseLeave={handleOnMouseLeave} > ( to, }) => { const [isHover, setIsHover] = useState(false); + const handleMouseEnter = useCallback(() => setIsHover(true), [setIsHover]); + const handleMouseLeave = useCallback(() => setIsHover(false), [setIsHover]); const isBarChartDataAvailable = barChart && barChart.length && @@ -218,9 +220,10 @@ export const StatItemsComponent = React.memo( areaChart && areaChart.length && areaChart.every(item => item.value != null && item.value.length > 0); + return ( - setIsHover(true)} onMouseLeave={() => setIsHover(false)}> + diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/activity_monitor/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/activity_monitor/index.tsx index d7306b8630bc2d..2d48d64acaecf7 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/activity_monitor/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/activity_monitor/index.tsx @@ -5,7 +5,7 @@ */ import { EuiBasicTable, EuiPanel, EuiSpacer } from '@elastic/eui'; -import React, { useState } from 'react'; +import React, { useState, useCallback } from 'react'; import { HeaderSection } from '../../../../components/header_section'; import { UtilityBar, @@ -278,6 +278,14 @@ export const ActivityMonitor = React.memo(() => { // const [selectedState, setSelectedState] = useState([]); const [sortState, setSortState] = useState({ field: 'ran', direction: 'desc' }); + const handleChange = useCallback( + ({ page, sort }: { page: PageTypes; sort: SortTypes }) => { + setPageState(page); + setSortState(sort); + }, + [setPageState, setSortState] + ); + return ( <> @@ -308,10 +316,7 @@ export const ActivityMonitor = React.memo(() => { isSelectable itemId="id" items={sampleTableData} - onChange={({ page, sort }: { page: PageTypes; sort: SortTypes }) => { - setPageState(page); - setSortState(sort); - }} + onChange={handleChange} pagination={{ pageIndex: pageState.index, pageSize: pageState.size, diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all_rules/columns.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all_rules/columns.tsx index cae0fb3eaf9065..9575ee736dea3b 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all_rules/columns.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all_rules/columns.tsx @@ -21,7 +21,7 @@ import { Action } from './reducer'; import { TableData } from '../types'; import * as i18n from '../translations'; import { PreferenceFormattedDate } from '../../../../components/formatted_date'; -import { RuleSwitch } from '../components/rule_switch'; +import { RuleSwitch, RuleStateChangeCallback } from '../components/rule_switch'; const getActions = (dispatch: React.Dispatch, kbnVersion: string) => [ { @@ -147,16 +147,20 @@ export const getColumns = (dispatch: React.Dispatch, kbnVersion: string) align: 'center', field: 'activate', name: i18n.COLUMN_ACTIVATE, - render: (value: TableData['activate'], item: TableData) => ( - { - await enableRulesAction([id], enabled, dispatch, kbnVersion); - }} - /> - ), + render: (value: TableData['activate'], item: TableData) => { + const handleRuleStateChange: RuleStateChangeCallback = async (enabled, id) => { + await enableRulesAction([id], enabled, dispatch, kbnVersion); + }; + + return ( + + ); + }, sortable: true, width: '85px', }, diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_switch/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_switch/index.tsx index 19523752f4f4a9..6d9b0a36f8548b 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_switch/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/rule_switch/index.tsx @@ -5,7 +5,7 @@ */ import styled from 'styled-components'; -import React from 'react'; +import React, { useCallback } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiSwitch } from '@elastic/eui'; const StaticSwitch = styled(EuiSwitch)` @@ -17,11 +17,13 @@ const StaticSwitch = styled(EuiSwitch)` StaticSwitch.displayName = 'StaticSwitch'; +export type RuleStateChangeCallback = (isEnabled: boolean, id: string) => void; + export interface RuleSwitchProps { id: string; enabled: boolean; isLoading: boolean; - onRuleStateChange: (isEnabled: boolean, id: string) => void; + onRuleStateChange: RuleStateChangeCallback; } /** @@ -32,26 +34,32 @@ export const RuleSwitchComponent = ({ enabled, isLoading, onRuleStateChange, -}: RuleSwitchProps) => ( - - - {isLoading ? ( - - ) : ( - { - onRuleStateChange(e.target.checked!, id); - }} - /> - )} - - -); +}: RuleSwitchProps) => { + const handleChange = useCallback( + e => { + onRuleStateChange(e.target.checked!, id); + }, + [onRuleStateChange, id] + ); + return ( + + + {isLoading ? ( + + ) : ( + + )} + + + ); +}; export const RuleSwitch = React.memo(RuleSwitchComponent); diff --git a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx index 97f0a21928a8af..a545be447796dc 100644 --- a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import * as React from 'react'; +import React from 'react'; import { Redirect, Route, Switch } from 'react-router-dom'; import styled from 'styled-components'; diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.test.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.test.tsx index febf9630e968a5..8d45bbbe34d33d 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.test.tsx @@ -63,7 +63,7 @@ describe('body', () => { from={0} isInitializing={false} detailName={'host-1'} - setQuery={() => {}} + setQuery={jest.fn()} to={0} setAbsoluteRangeDatePicker={(jest.fn() as unknown) as SetAbsoluteRangeDatePicker} hostDetailsPagePath={hostDetailsPagePath} diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.tsx index 1252c7031e8a5f..a09e21f2d1a352 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/details_tabs.tsx @@ -45,14 +45,14 @@ const HostDetailsTabs = React.memo( to: fromTo.to, }); }, - [setAbsoluteRangeDatePicker, scoreIntervalToDateTime] + [setAbsoluteRangeDatePicker] ); const updateDateRange = useCallback( (min: number, max: number) => { setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); }, - [setAbsoluteRangeDatePicker, scoreIntervalToDateTime] + [setAbsoluteRangeDatePicker] ); const tabProps = { diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx index d30665c5a21426..d8bcc6fe3c294d 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx @@ -5,7 +5,7 @@ */ import { EuiHorizontalRule, EuiSpacer } from '@elastic/eui'; -import React, { useContext, useEffect } from 'react'; +import React, { useContext, useEffect, useCallback } from 'react'; import { connect } from 'react-redux'; import { StickyContainer } from 'react-sticky'; import { compose } from 'redux'; @@ -61,9 +61,15 @@ const HostDetailsComponent = React.memo( }) => { useEffect(() => { setHostDetailsTablesActivePageToZero(null); - }, [detailName]); + }, [setHostDetailsTablesActivePageToZero, detailName]); const capabilities = useContext(MlCapabilitiesContext); const core = useKibanaCore(); + const narrowDateRange = useCallback( + (min: number, max: number) => { + setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); + }, + [setAbsoluteRangeDatePicker] + ); return ( <> @@ -176,9 +182,7 @@ const HostDetailsComponent = React.memo( refetch={refetch} setQuery={setQuery} to={to} - narrowDateRange={(min: number, max: number) => { - setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); - }} + narrowDateRange={narrowDateRange} /> )} diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx index 4ff666464404e2..0c058f25854c01 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx @@ -5,7 +5,7 @@ */ import { EuiSpacer } from '@elastic/eui'; -import * as React from 'react'; +import React, { useCallback } from 'react'; import { connect } from 'react-redux'; import { StickyContainer } from 'react-sticky'; import { compose } from 'redux'; @@ -52,6 +52,12 @@ const HostsComponent = React.memo( }) => { const capabilities = React.useContext(MlCapabilitiesContext); const core = useKibanaCore(); + const narrowDateRange = useCallback( + (min: number, max: number) => { + setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); + }, + [setAbsoluteRangeDatePicker] + ); return ( <> @@ -93,9 +99,7 @@ const HostsComponent = React.memo( refetch={refetch} setQuery={setQuery} to={to} - narrowDateRange={(min: number, max: number) => { - setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); - }} + narrowDateRange={narrowDateRange} /> )} diff --git a/x-pack/legacy/plugins/siem/public/pages/network/index.tsx b/x-pack/legacy/plugins/siem/public/pages/network/index.tsx index 009a89bb3889f6..1bc3d9a054bb89 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext } from 'react'; +import React, { useContext, useMemo } from 'react'; import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom'; import { MlCapabilitiesContext } from '../../components/ml/permissions/ml_capabilities_provider'; @@ -24,6 +24,14 @@ const ipDetailsPagePath = `${networkPagePath}/ip/:detailName`; export const NetworkContainer = React.memo(() => { const capabilities = useContext(MlCapabilitiesContext); + const capabilitiesFetched = capabilities.capabilitiesFetched; + const userHasMlUserPermissions = useMemo(() => hasMlUserPermissions(capabilities), [ + capabilities, + ]); + const networkRoutePath = useMemo( + () => getNetworkRoutePath(networkPagePath, capabilitiesFetched, userHasMlUserPermissions), + [capabilitiesFetched, userHasMlUserPermissions] + ); return ( @@ -31,11 +39,7 @@ export const NetworkContainer = React.memo(() => { ( (() => { deleteQuery={deleteQuery} isInitializing={isInitializing} capabilitiesFetched={capabilities.capabilitiesFetched} - hasMlUserPermissions={hasMlUserPermissions(capabilities)} + hasMlUserPermissions={userHasMlUserPermissions} /> )} /> diff --git a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx index 75ca5a5dfe1a68..97db422b539e84 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx @@ -68,13 +68,13 @@ export const IPDetailsComponent = ({ to: fromTo.to, }); }, - [scoreIntervalToDateTime, setAbsoluteRangeDatePicker] + [setAbsoluteRangeDatePicker] ); const core = useKibanaCore(); useEffect(() => { setIpDetailsTablesActivePageToZero(null); - }, [detailName]); + }, [detailName, setIpDetailsTablesActivePageToZero]); return ( <> @@ -134,14 +134,7 @@ export const IPDetailsComponent = ({ setQuery={setQuery} startDate={from} endDate={to} - narrowDateRange={(score, interval) => { - const fromTo = scoreIntervalToDateTime(score, interval); - setAbsoluteRangeDatePicker({ - id: 'global', - from: fromTo.from, - to: fromTo.to, - }); - }} + narrowDateRange={narrowDateRange} /> )} diff --git a/x-pack/legacy/plugins/siem/public/pages/network/navigation/network_routes.tsx b/x-pack/legacy/plugins/siem/public/pages/network/navigation/network_routes.tsx index 681e1f8e1e34d7..d1c792ade09852 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/navigation/network_routes.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/navigation/network_routes.tsx @@ -42,13 +42,13 @@ export const NetworkRoutes = ({ to: fromTo.to, }); }, - [scoreIntervalToDateTime, setAbsoluteRangeDatePicker] + [setAbsoluteRangeDatePicker] ); const updateDateRange = useCallback( (min: number, max: number) => { setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); }, - [from, to] + [setAbsoluteRangeDatePicker] ); const networkAnomaliesFilterQuery = { diff --git a/x-pack/legacy/plugins/siem/public/pages/network/network.tsx b/x-pack/legacy/plugins/siem/public/pages/network/network.tsx index aa7572e5741bda..116664fef6ddcc 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/network.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/network.tsx @@ -5,7 +5,7 @@ */ import { EuiSpacer } from '@elastic/eui'; -import React from 'react'; +import React, { useCallback } from 'react'; import { connect } from 'react-redux'; import { StickyContainer } from 'react-sticky'; @@ -49,6 +49,12 @@ const NetworkComponent = React.memo( capabilitiesFetched, }) => { const core = useKibanaCore(); + const narrowDateRange = useCallback( + (min: number, max: number) => { + setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); + }, + [setAbsoluteRangeDatePicker] + ); return ( <> @@ -101,9 +107,7 @@ const NetworkComponent = React.memo( loading={loading} from={from} to={to} - narrowDateRange={(min: number, max: number) => { - setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); - }} + narrowDateRange={narrowDateRange} /> )}