[7.7] [SIEM] [Cases] Insert timeline and reporters/tags in table bug fixes (#63642) (#63658)

* [SIEM] [Cases] Insert timeline and reporters/tags in table bug fixes (#63642)

* fix type err
This commit is contained in:
Steph Milovic 2020-04-16 08:05:11 -06:00 committed by GitHub
parent 9ef8a99779
commit a9b3d3036c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 70 additions and 16 deletions

View file

@ -26,6 +26,7 @@ const mockLocationWithState = {
state: {
insertTimeline: {
timelineId: 'timeline-id',
timelineSavedObjectId: '34578-3497-5893-47589-34759',
timelineTitle: 'Timeline title',
},
},
@ -49,7 +50,7 @@ describe('Insert timeline popover ', () => {
payload: { id: 'timeline-id', show: false },
type: 'x-pack/siem/local/timeline/SHOW_TIMELINE',
});
expect(onTimelineChange).toBeCalledWith('Timeline title', 'timeline-id');
expect(onTimelineChange).toBeCalledWith('Timeline title', '34578-3497-5893-47589-34759');
});
it('should do nothing when router state', () => {
jest.spyOn(routeData, 'useLocation').mockReturnValue(mockLocation);

View file

@ -23,6 +23,7 @@ interface InsertTimelinePopoverProps {
interface RouterState {
insertTimeline: {
timelineId: string;
timelineSavedObjectId: string;
timelineTitle: string;
};
}
@ -46,7 +47,7 @@ export const InsertTimelinePopoverComponent: React.FC<Props> = ({
);
onTimelineChange(
routerState.insertTimeline.timelineTitle,
routerState.insertTimeline.timelineId
routerState.insertTimeline.timelineSavedObjectId
);
setRouterState(null);
}

View file

@ -21,6 +21,7 @@ import React, { useCallback } from 'react';
import uuid from 'uuid';
import styled from 'styled-components';
import { useHistory } from 'react-router-dom';
import { useSelector } from 'react-redux';
import { Note } from '../../../lib/note';
import { Notes } from '../../notes';
@ -29,6 +30,8 @@ import { NOTES_PANEL_WIDTH } from './notes_size';
import { ButtonContainer, DescriptionContainer, LabelText, NameField, StyledStar } from './styles';
import * as i18n from './translations';
import { SiemPageName } from '../../../pages/home/types';
import { timelineSelectors } from '../../../store/timeline';
import { State } from '../../../store';
export const historyToolTip = 'The chronological history of actions related to this timeline';
export const streamLiveToolTip = 'Update the Timeline as new data arrives';
@ -121,6 +124,9 @@ interface NewCaseProps {
export const NewCase = React.memo<NewCaseProps>(({ onClosePopover, timelineId, timelineTitle }) => {
const history = useHistory();
const { savedObjectId } = useSelector((state: State) =>
timelineSelectors.selectTimeline(state, timelineId)
);
const handleClick = useCallback(() => {
onClosePopover();
history.push({
@ -128,6 +134,7 @@ export const NewCase = React.memo<NewCaseProps>(({ onClosePopover, timelineId, t
state: {
insertTimeline: {
timelineId,
timelineSavedObjectId: savedObjectId,
timelineTitle: timelineTitle.length > 0 ? timelineTitle : i18n.UNTITLED_TIMELINE,
},
},

View file

@ -20,6 +20,10 @@ type Action =
| { type: 'FETCH_SUCCESS'; payload: string[] }
| { type: 'FETCH_FAILURE' };
export interface UseGetTags extends TagsState {
fetchTags: () => void;
}
const dataFetchReducer = (state: TagsState, action: Action): TagsState => {
switch (action.type) {
case 'FETCH_INIT':
@ -47,7 +51,7 @@ const dataFetchReducer = (state: TagsState, action: Action): TagsState => {
};
const initialData: string[] = [];
export const useGetTags = (): TagsState => {
export const useGetTags = (): UseGetTags => {
const [state, dispatch] = useReducer(dataFetchReducer, {
isLoading: false,
isError: false,
@ -55,7 +59,7 @@ export const useGetTags = (): TagsState => {
});
const [, dispatchToaster] = useStateToaster();
useEffect(() => {
const callFetch = () => {
let didCancel = false;
const abortCtrl = new AbortController();
@ -82,6 +86,9 @@ export const useGetTags = (): TagsState => {
abortCtrl.abort();
didCancel = true;
};
};
useEffect(() => {
callFetch();
}, []);
return state;
return { ...state, fetchTags: callFetch };
};

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
EuiBasicTable,
EuiButton,
@ -129,13 +129,25 @@ export const AllCases = React.memo<AllCasesProps>(({ userCanCrud }) => {
id: '',
});
const [deleteBulk, setDeleteBulk] = useState<DeleteCase[]>([]);
const refreshCases = useCallback(() => {
refetchCases(filterOptions, queryParams);
fetchCasesStatus();
setSelectedCases([]);
setDeleteBulk([]);
}, [filterOptions, queryParams]);
const filterRefetch = useRef<() => void>();
const setFilterRefetch = useCallback(
(refetchFilter: () => void) => {
filterRefetch.current = refetchFilter;
},
[filterRefetch.current]
);
const refreshCases = useCallback(
(dataRefresh = true) => {
if (dataRefresh) refetchCases(filterOptions, queryParams);
fetchCasesStatus();
setSelectedCases([]);
setDeleteBulk([]);
if (filterRefetch.current != null) {
filterRefetch.current();
}
},
[filterOptions, queryParams, filterRefetch.current]
);
useEffect(() => {
if (isDeleted) {
@ -247,6 +259,7 @@ export const AllCases = React.memo<AllCasesProps>(({ userCanCrud }) => {
};
}
setQueryParams(newQueryParams);
refreshCases(false);
},
[queryParams]
);
@ -259,6 +272,7 @@ export const AllCases = React.memo<AllCasesProps>(({ userCanCrud }) => {
setQueryParams({ sortField: SortFieldCase.createdAt });
}
setFilters(newFilterOptions);
refreshCases(false);
},
[filterOptions, queryParams]
);
@ -347,6 +361,7 @@ export const AllCases = React.memo<AllCasesProps>(({ userCanCrud }) => {
tags: filterOptions.tags,
status: filterOptions.status,
}}
setFilterRefetch={setFilterRefetch}
/>
{isCasesLoading && isDataEmpty ? (
<Div>

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { useCallback, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { isEqual } from 'lodash/fp';
import {
EuiFieldSearch,
@ -25,6 +25,7 @@ interface CasesTableFiltersProps {
countOpenCases: number | null;
onFilterChanged: (filterOptions: Partial<FilterOptions>) => void;
initial: FilterOptions;
setFilterRefetch: (val: () => void) => void;
}
/**
@ -41,6 +42,7 @@ const CasesTableFiltersComponent = ({
countOpenCases,
onFilterChanged,
initial = defaultInitial,
setFilterRefetch,
}: CasesTableFiltersProps) => {
const [selectedReporters, setselectedReporters] = useState(
initial.reporters.map(r => r.full_name ?? r.username ?? '')
@ -48,8 +50,29 @@ const CasesTableFiltersComponent = ({
const [search, setSearch] = useState(initial.search);
const [selectedTags, setSelectedTags] = useState(initial.tags);
const [showOpenCases, setShowOpenCases] = useState(initial.status === 'open');
const { tags } = useGetTags();
const { reporters, respReporters } = useGetReporters();
const { tags, fetchTags } = useGetTags();
const { reporters, respReporters, fetchReporters } = useGetReporters();
const refetch = useCallback(() => {
fetchTags();
fetchReporters();
}, [fetchReporters, fetchTags]);
useEffect(() => {
if (setFilterRefetch != null) {
setFilterRefetch(refetch);
}
}, [refetch, setFilterRefetch]);
useEffect(() => {
if (selectedReporters.length) {
const newReporters = selectedReporters.filter(r => reporters.includes(r));
handleSelectedReporters(newReporters);
}
}, [reporters]);
useEffect(() => {
if (selectedTags.length) {
const newTags = selectedTags.filter(t => tags.includes(t));
handleSelectedTags(newTags);
}
}, [tags]);
const handleSelectedReporters = useCallback(
newReporters => {