Skip to content

Commit 530f5ca

Browse files
committed
disablebulk actions for immutable timeline templates
1 parent 1cb177f commit 530f5ca

File tree

6 files changed

+106
-18
lines changed

6 files changed

+106
-18
lines changed

x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ export const StatefulOpenTimelineComponent = React.memo<OpenTimelineOwnProps>(
329329
sortField={sortField}
330330
templateTimelineFilter={templateTimelineFilter}
331331
timelineType={timelineType}
332+
timelineStatus={timelineStatus}
332333
timelineFilter={timelineTabs}
333334
title={title}
334335
totalSearchResultsCount={totalCount}
@@ -359,6 +360,7 @@ export const StatefulOpenTimelineComponent = React.memo<OpenTimelineOwnProps>(
359360
sortField={sortField}
360361
templateTimelineFilter={templateTimelineFilter}
361362
timelineType={timelineType}
363+
timelineStatus={timelineStatus}
362364
timelineFilter={timelineFilters}
363365
title={title}
364366
totalSearchResultsCount={totalCount}

x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.test.tsx

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { TimelinesTableProps } from './timelines_table';
1717
import { mockTimelineResults } from '../../../common/mock/timeline_results';
1818
import { OpenTimeline } from './open_timeline';
1919
import { DEFAULT_SORT_DIRECTION, DEFAULT_SORT_FIELD } from './constants';
20-
import { TimelineType } from '../../../../common/types/timeline';
20+
import { TimelineType, TimelineStatus } from '../../../../common/types/timeline';
2121

2222
jest.mock('../../../common/lib/kibana');
2323

@@ -50,6 +50,7 @@ describe('OpenTimeline', () => {
5050
sortField: DEFAULT_SORT_FIELD,
5151
title,
5252
timelineType: TimelineType.default,
53+
timelineStatus: TimelineStatus.active,
5354
templateTimelineFilter: [<div />],
5455
totalSearchResultsCount: mockSearchResults.length,
5556
});
@@ -263,4 +264,48 @@ describe('OpenTimeline', () => {
263264
`Showing: ${mockResults.length} timelines with "How was your day?"`
264265
);
265266
});
267+
268+
test('it should render bulk actions if timelineStatus is not immutable', () => {
269+
const defaultProps = {
270+
...getDefaultTestProps(mockResults),
271+
timelineStatus: null,
272+
};
273+
const wrapper = mountWithIntl(
274+
<ThemeProvider theme={theme}>
275+
<OpenTimeline {...defaultProps} />
276+
</ThemeProvider>
277+
);
278+
279+
expect(wrapper.find('[data-test-subj="utility-bar-action"]').exists()).toEqual(true);
280+
});
281+
282+
test('it should not render bulk actions if timelineStatus is immutable', () => {
283+
const defaultProps = {
284+
...getDefaultTestProps(mockResults),
285+
timelineStatus: TimelineStatus.immutable,
286+
};
287+
const wrapper = mountWithIntl(
288+
<ThemeProvider theme={theme}>
289+
<OpenTimeline {...defaultProps} />
290+
</ThemeProvider>
291+
);
292+
293+
expect(wrapper.find('[data-test-subj="utility-bar-action"]').exists()).toEqual(false);
294+
});
295+
296+
test('it should not render a selectable timeline table if timelineStatus is immutable', () => {
297+
const defaultProps = {
298+
...getDefaultTestProps(mockResults),
299+
timelineStatus: TimelineStatus.immutable,
300+
};
301+
const wrapper = mountWithIntl(
302+
<ThemeProvider theme={theme}>
303+
<OpenTimeline {...defaultProps} />
304+
</ThemeProvider>
305+
);
306+
307+
expect(
308+
wrapper.find('[data-test-subj="timelines-table"]').first().prop('actionTimelineToShow')
309+
).toEqual(['createFrom', 'duplicate']);
310+
});
266311
});

x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline.tsx

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { EuiPanel, EuiBasicTable } from '@elastic/eui';
88
import React, { useCallback, useMemo, useRef } from 'react';
99
import { FormattedMessage } from '@kbn/i18n/react';
1010

11-
import { TimelineType } from '../../../../common/types/timeline';
11+
import { TimelineType, TimelineStatus } from '../../../../common/types/timeline';
1212
import { ImportDataModal } from '../../../common/components/import_data_modal';
1313
import {
1414
UtilityBarGroup,
@@ -55,6 +55,7 @@ export const OpenTimeline = React.memo<OpenTimelineProps>(
5555
setImportDataModalToggle,
5656
sortField,
5757
timelineType = TimelineType.default,
58+
timelineStatus,
5859
timelineFilter,
5960
templateTimelineFilter,
6061
totalSearchResultsCount,
@@ -140,19 +141,23 @@ export const OpenTimeline = React.memo<OpenTimelineProps>(
140141
}, [setImportDataModalToggle, refetch, searchResults, totalSearchResultsCount]);
141142

142143
const actionTimelineToShow = useMemo<ActionTimelineToShow[]>(() => {
143-
const timelineActions: ActionTimelineToShow[] = [
144-
'createFrom',
145-
'duplicate',
146-
'export',
147-
'selectable',
148-
];
144+
const timelineActions: ActionTimelineToShow[] = ['createFrom', 'duplicate'];
149145

150-
if (onDeleteSelected != null && deleteTimelines != null) {
146+
if (timelineStatus !== TimelineStatus.immutable) {
147+
timelineActions.push('export');
148+
timelineActions.push('selectable');
149+
}
150+
151+
if (
152+
onDeleteSelected != null &&
153+
deleteTimelines != null &&
154+
timelineStatus !== TimelineStatus.immutable
155+
) {
151156
timelineActions.push('delete');
152157
}
153158

154159
return timelineActions;
155-
}, [onDeleteSelected, deleteTimelines]);
160+
}, [onDeleteSelected, deleteTimelines, timelineStatus]);
156161

157162
const SearchRowContent = useMemo(() => <>{templateTimelineFilter}</>, [templateTimelineFilter]);
158163

@@ -213,13 +218,16 @@ export const OpenTimeline = React.memo<OpenTimelineProps>(
213218
? i18n.SELECTED_TEMPLATES(selectedItems.length)
214219
: i18n.SELECTED_TIMELINES(selectedItems.length)}
215220
</UtilityBarText>
216-
<UtilityBarAction
217-
iconSide="right"
218-
iconType="arrowDown"
219-
popoverContent={getBatchItemsPopoverContent}
220-
>
221-
{i18n.BATCH_ACTIONS}
222-
</UtilityBarAction>
221+
{timelineStatus !== TimelineStatus.immutable && (
222+
<UtilityBarAction
223+
iconSide="right"
224+
iconType="arrowDown"
225+
popoverContent={getBatchItemsPopoverContent}
226+
data-test-subj="utility-bar-action"
227+
>
228+
{i18n.BATCH_ACTIONS}
229+
</UtilityBarAction>
230+
)}
223231
<UtilityBarAction iconSide="right" iconType="refresh" onClick={onRefreshBtnClick}>
224232
{i18n.REFRESH}
225233
</UtilityBarAction>

x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline_modal/open_timeline_modal_body.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { TimelinesTableProps } from '../timelines_table';
1717
import { mockTimelineResults } from '../../../../common/mock/timeline_results';
1818
import { OpenTimelineModalBody } from './open_timeline_modal_body';
1919
import { DEFAULT_SORT_DIRECTION, DEFAULT_SORT_FIELD } from '../constants';
20-
import { TimelineType } from '../../../../../common/types/timeline';
20+
import { TimelineType, TimelineStatus } from '../../../../../common/types/timeline';
2121

2222
jest.mock('../../../../common/lib/kibana');
2323

@@ -48,6 +48,7 @@ describe('OpenTimelineModal', () => {
4848
sortDirection: DEFAULT_SORT_DIRECTION,
4949
sortField: DEFAULT_SORT_FIELD,
5050
timelineType: TimelineType.default,
51+
timelineStatus: TimelineStatus.active,
5152
templateTimelineFilter: [<div />],
5253
title,
5354
totalSearchResultsCount: mockSearchResults.length,

x-pack/plugins/security_solution/public/timelines/components/open_timeline/timelines_table/actions_columns.test.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import React from 'react';
1212
import { ThemeProvider } from 'styled-components';
1313

1414
import '../../../../common/mock/match_media';
15+
1516
import { mockTimelineResults } from '../../../../common/mock/timeline_results';
1617
import { OpenTimelineResult } from '../types';
1718
import { TimelinesTableProps } from '.';
@@ -233,4 +234,32 @@ describe('#getActionsColumns', () => {
233234

234235
expect(enableExportTimelineDownloader).toBeCalledWith(mockResults[0]);
235236
});
237+
238+
test('it should not render "export timeline" if it is not included', () => {
239+
const testProps: TimelinesTableProps = {
240+
...getMockTimelinesTableProps(mockResults),
241+
actionTimelineToShow: ['createFrom', 'duplicate'],
242+
};
243+
const wrapper = mountWithIntl(
244+
<ThemeProvider theme={theme}>
245+
<TimelinesTable {...testProps} />
246+
</ThemeProvider>
247+
);
248+
249+
expect(wrapper.find('[data-test-subj="export-timeline"]').exists()).toEqual(false);
250+
});
251+
252+
test('it should not render "delete timeline" if it is not included', () => {
253+
const testProps: TimelinesTableProps = {
254+
...getMockTimelinesTableProps(mockResults),
255+
actionTimelineToShow: ['createFrom', 'duplicate'],
256+
};
257+
const wrapper = mountWithIntl(
258+
<ThemeProvider theme={theme}>
259+
<TimelinesTable {...testProps} />
260+
</ThemeProvider>
261+
);
262+
263+
expect(wrapper.find('[data-test-subj="delete-timeline"]').exists()).toEqual(false);
264+
});
236265
});

x-pack/plugins/security_solution/public/timelines/components/open_timeline/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
TimelineStatus,
1515
TemplateTimelineTypeLiteral,
1616
RowRendererId,
17+
TimelineStatusLiteralWithNull,
1718
} from '../../../../common/types/timeline';
1819

1920
/** The users who added a timeline to favorites */
@@ -174,6 +175,8 @@ export interface OpenTimelineProps {
174175
sortField: string;
175176
/** this affects timeline's behaviour like editable / duplicatible */
176177
timelineType: TimelineTypeLiteralWithNull;
178+
/* active or immutable */
179+
timelineStatus: TimelineStatusLiteralWithNull;
177180
/** when timelineType === template, templatetimelineFilter is a JSX.Element */
178181
templateTimelineFilter: JSX.Element[] | null;
179182
/** timeline / timeline template */

0 commit comments

Comments
 (0)