Skip to content

Commit cee743d

Browse files
[SIEM] Fix draft timeline can be attached to a case (#67844)
1 parent c1edded commit cee743d

6 files changed

Lines changed: 118 additions & 29 deletions

File tree

x-pack/plugins/siem/public/timelines/components/flyout/header/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ const StatefulFlyoutHeader = React.memo<Props>(
3939
title,
4040
noteIds,
4141
notesById,
42+
status,
4243
timelineId,
4344
toggleLock,
4445
updateDescription,
@@ -62,6 +63,7 @@ const StatefulFlyoutHeader = React.memo<Props>(
6263
isFavorite={isFavorite}
6364
title={title}
6465
noteIds={noteIds}
66+
status={status}
6567
timelineId={timelineId}
6668
toggleLock={toggleLock}
6769
updateDescription={updateDescription}
@@ -94,6 +96,7 @@ const makeMapStateToProps = () => {
9496
kqlQuery,
9597
title = '',
9698
noteIds = emptyNotesId,
99+
status,
97100
} = timeline;
98101

99102
const history = emptyHistory; // TODO: get history from store via selector
@@ -107,6 +110,7 @@ const makeMapStateToProps = () => {
107110
isFavorite,
108111
isDatepickerLocked: globalInput.linkTo.includes('timeline'),
109112
noteIds,
113+
status,
110114
title,
111115
};
112116
};

x-pack/plugins/siem/public/timelines/components/timeline/properties/helpers.tsx

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import styled from 'styled-components';
2323
import { useHistory } from 'react-router-dom';
2424
import { useSelector } from 'react-redux';
2525

26+
import { TimelineStatus } from '../../../../../common/types/timeline';
2627
import { Note } from '../../../../common/lib/note';
2728
import { Notes } from '../../notes';
2829
import { AssociateNote, UpdateNote } from '../../notes/helpers';
@@ -119,40 +120,44 @@ Name.displayName = 'Name';
119120
interface NewCaseProps {
120121
onClosePopover: () => void;
121122
timelineId: string;
123+
timelineStatus: TimelineStatus;
122124
timelineTitle: string;
123125
}
124126

125-
export const NewCase = React.memo<NewCaseProps>(({ onClosePopover, timelineId, timelineTitle }) => {
126-
const history = useHistory();
127-
const { savedObjectId } = useSelector((state: State) =>
128-
timelineSelectors.selectTimeline(state, timelineId)
129-
);
130-
const handleClick = useCallback(() => {
131-
onClosePopover();
132-
history.push({
133-
pathname: `/${SiemPageName.case}/create`,
134-
state: {
135-
insertTimeline: {
136-
timelineId,
137-
timelineSavedObjectId: savedObjectId,
138-
timelineTitle: timelineTitle.length > 0 ? timelineTitle : i18n.UNTITLED_TIMELINE,
127+
export const NewCase = React.memo<NewCaseProps>(
128+
({ onClosePopover, timelineId, timelineStatus, timelineTitle }) => {
129+
const history = useHistory();
130+
const { savedObjectId } = useSelector((state: State) =>
131+
timelineSelectors.selectTimeline(state, timelineId)
132+
);
133+
const handleClick = useCallback(() => {
134+
onClosePopover();
135+
history.push({
136+
pathname: `/${SiemPageName.case}/create`,
137+
state: {
138+
insertTimeline: {
139+
timelineId,
140+
timelineSavedObjectId: savedObjectId,
141+
timelineTitle: timelineTitle.length > 0 ? timelineTitle : i18n.UNTITLED_TIMELINE,
142+
},
139143
},
140-
},
141-
});
142-
}, [onClosePopover, history, timelineId, timelineTitle]);
144+
});
145+
}, [onClosePopover, history, timelineId, timelineTitle]);
143146

144-
return (
145-
<EuiButtonEmpty
146-
data-test-subj="attach-timeline-case"
147-
color="text"
148-
iconSide="left"
149-
iconType="paperClip"
150-
onClick={handleClick}
151-
>
152-
{i18n.ATTACH_TIMELINE_TO_NEW_CASE}
153-
</EuiButtonEmpty>
154-
);
155-
});
147+
return (
148+
<EuiButtonEmpty
149+
data-test-subj="attach-timeline-case"
150+
color="text"
151+
iconSide="left"
152+
iconType="paperClip"
153+
disabled={timelineStatus === TimelineStatus.draft}
154+
onClick={handleClick}
155+
>
156+
{i18n.ATTACH_TIMELINE_TO_NEW_CASE}
157+
</EuiButtonEmpty>
158+
);
159+
}
160+
);
156161
NewCase.displayName = 'NewCase';
157162

158163
interface NewTimelineProps {

x-pack/plugins/siem/public/timelines/components/timeline/properties/index.test.tsx

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { mount } from 'enzyme';
88
import React from 'react';
99
import { Provider as ReduxStoreProvider } from 'react-redux';
1010

11+
import { TimelineStatus } from '../../../../../common/types/timeline';
1112
import {
1213
mockGlobalState,
1314
apolloClientObservable,
@@ -25,6 +26,24 @@ jest.mock('../../../../common/components/utils');
2526
width: mockedWidth,
2627
}));
2728

29+
jest.mock('react-redux', () => {
30+
const originalModule = jest.requireActual('react-redux');
31+
32+
return {
33+
...originalModule,
34+
useSelector: jest.fn().mockReturnValue({ savedObjectId: '1' }),
35+
};
36+
});
37+
38+
jest.mock('react-router-dom', () => {
39+
const originalModule = jest.requireActual('react-router-dom');
40+
41+
return {
42+
...originalModule,
43+
useHistory: jest.fn(),
44+
};
45+
});
46+
2847
describe('Properties', () => {
2948
const usersViewing = ['elastic'];
3049

@@ -50,6 +69,7 @@ describe('Properties', () => {
5069
description=""
5170
getNotesByIds={jest.fn()}
5271
noteIds={[]}
72+
status={TimelineStatus.active}
5373
timelineId="abc"
5474
toggleLock={jest.fn()}
5575
updateDescription={jest.fn()}
@@ -60,7 +80,45 @@ describe('Properties', () => {
6080
/>
6181
</ReduxStoreProvider>
6282
);
83+
84+
wrapper.find('[data-test-subj="settings-gear"]').at(0).simulate('click');
85+
6386
expect(wrapper.find('[data-test-subj="timeline-properties"]').exists()).toEqual(true);
87+
expect(wrapper.find('button[data-test-subj="attach-timeline-case"]').prop('disabled')).toEqual(
88+
false
89+
);
90+
});
91+
92+
test('renders correctly draft timeline', () => {
93+
const wrapper = mount(
94+
<ReduxStoreProvider store={store}>
95+
<Properties
96+
associateNote={jest.fn()}
97+
createTimeline={jest.fn()}
98+
isDataInTimeline={false}
99+
isDatepickerLocked={false}
100+
isFavorite={false}
101+
title=""
102+
description=""
103+
getNotesByIds={jest.fn()}
104+
noteIds={[]}
105+
status={TimelineStatus.draft}
106+
timelineId="abc"
107+
toggleLock={jest.fn()}
108+
updateDescription={jest.fn()}
109+
updateIsFavorite={jest.fn()}
110+
updateTitle={jest.fn()}
111+
updateNote={jest.fn()}
112+
usersViewing={usersViewing}
113+
/>
114+
</ReduxStoreProvider>
115+
);
116+
117+
wrapper.find('[data-test-subj="settings-gear"]').at(0).simulate('click');
118+
119+
expect(wrapper.find('button[data-test-subj="attach-timeline-case"]').prop('disabled')).toEqual(
120+
true
121+
);
64122
});
65123

66124
test('it renders an empty star icon when it is NOT a favorite', () => {
@@ -76,6 +134,7 @@ describe('Properties', () => {
76134
description=""
77135
getNotesByIds={jest.fn()}
78136
noteIds={[]}
137+
status={TimelineStatus.active}
79138
timelineId="abc"
80139
toggleLock={jest.fn()}
81140
updateDescription={jest.fn()}
@@ -103,6 +162,7 @@ describe('Properties', () => {
103162
description=""
104163
getNotesByIds={jest.fn()}
105164
noteIds={[]}
165+
status={TimelineStatus.active}
106166
timelineId="abc"
107167
toggleLock={jest.fn()}
108168
updateDescription={jest.fn()}
@@ -132,6 +192,7 @@ describe('Properties', () => {
132192
description=""
133193
getNotesByIds={jest.fn()}
134194
noteIds={[]}
195+
status={TimelineStatus.active}
135196
timelineId="abc"
136197
toggleLock={jest.fn()}
137198
updateDescription={jest.fn()}
@@ -159,6 +220,7 @@ describe('Properties', () => {
159220
description=""
160221
getNotesByIds={jest.fn()}
161222
noteIds={[]}
223+
status={TimelineStatus.active}
162224
timelineId="abc"
163225
toggleLock={jest.fn()}
164226
updateDescription={jest.fn()}
@@ -191,6 +253,7 @@ describe('Properties', () => {
191253
description=""
192254
getNotesByIds={jest.fn()}
193255
noteIds={[]}
256+
status={TimelineStatus.active}
194257
timelineId="abc"
195258
toggleLock={jest.fn()}
196259
updateDescription={jest.fn()}
@@ -222,6 +285,7 @@ describe('Properties', () => {
222285
description=""
223286
getNotesByIds={jest.fn()}
224287
noteIds={[]}
288+
status={TimelineStatus.active}
225289
timelineId="abc"
226290
toggleLock={jest.fn()}
227291
updateDescription={jest.fn()}
@@ -256,6 +320,7 @@ describe('Properties', () => {
256320
description={description}
257321
getNotesByIds={jest.fn()}
258322
noteIds={[]}
323+
status={TimelineStatus.active}
259324
timelineId="abc"
260325
toggleLock={jest.fn()}
261326
updateDescription={jest.fn()}
@@ -292,6 +357,7 @@ describe('Properties', () => {
292357
description={description}
293358
getNotesByIds={jest.fn()}
294359
noteIds={[]}
360+
status={TimelineStatus.active}
295361
timelineId="abc"
296362
toggleLock={jest.fn()}
297363
updateDescription={jest.fn()}
@@ -326,6 +392,7 @@ describe('Properties', () => {
326392
description=""
327393
getNotesByIds={jest.fn()}
328394
noteIds={[]}
395+
status={TimelineStatus.active}
329396
timelineId="abc"
330397
toggleLock={jest.fn()}
331398
updateDescription={jest.fn()}
@@ -360,6 +427,7 @@ describe('Properties', () => {
360427
description=""
361428
getNotesByIds={jest.fn()}
362429
noteIds={[]}
430+
status={TimelineStatus.active}
363431
timelineId="abc"
364432
toggleLock={jest.fn()}
365433
updateDescription={jest.fn()}
@@ -392,6 +460,7 @@ describe('Properties', () => {
392460
description=""
393461
getNotesByIds={jest.fn()}
394462
noteIds={[]}
463+
status={TimelineStatus.active}
395464
timelineId="abc"
396465
toggleLock={jest.fn()}
397466
updateDescription={jest.fn()}
@@ -421,6 +490,7 @@ describe('Properties', () => {
421490
description=""
422491
getNotesByIds={jest.fn()}
423492
noteIds={[]}
493+
status={TimelineStatus.active}
424494
timelineId="abc"
425495
toggleLock={jest.fn()}
426496
updateDescription={jest.fn()}
@@ -448,6 +518,7 @@ describe('Properties', () => {
448518
description=""
449519
getNotesByIds={jest.fn()}
450520
noteIds={[]}
521+
status={TimelineStatus.active}
451522
timelineId="abc"
452523
toggleLock={jest.fn()}
453524
updateDescription={jest.fn()}

x-pack/plugins/siem/public/timelines/components/timeline/properties/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import React, { useState, useCallback, useMemo } from 'react';
88

9+
import { TimelineStatus } from '../../../../../common/types/timeline';
910
import { useThrottledResizeObserver } from '../../../../common/components/utils';
1011
import { Note } from '../../../../common/lib/note';
1112
import { InputsModelId } from '../../../../common/store/inputs/constants';
@@ -31,6 +32,7 @@ interface Props {
3132
isFavorite: boolean;
3233
noteIds: string[];
3334
timelineId: string;
35+
status: TimelineStatus;
3436
title: string;
3537
toggleLock: ToggleLock;
3638
updateDescription: UpdateDescription;
@@ -62,6 +64,7 @@ export const Properties = React.memo<Props>(
6264
isDatepickerLocked,
6365
isFavorite,
6466
noteIds,
67+
status,
6568
timelineId,
6669
title,
6770
toggleLock,
@@ -140,6 +143,7 @@ export const Properties = React.memo<Props>(
140143
showNotesFromWidth={width < showNotesThreshold}
141144
showTimelineModal={showTimelineModal}
142145
showUsersView={title.length > 0}
146+
status={status}
143147
timelineId={timelineId}
144148
title={title}
145149
updateDescription={updateDescription}

x-pack/plugins/siem/public/timelines/components/timeline/properties/properties_right.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { InspectButton, InspectButtonContainer } from '../../../../common/compon
2222
import * as i18n from './translations';
2323
import { AssociateNote } from '../../notes/helpers';
2424
import { Note } from '../../../../common/lib/note';
25+
import { TimelineStatus } from '../../../../../common/types/timeline';
2526

2627
export const PropertiesRightStyle = styled(EuiFlexGroup)`
2728
margin-right: 5px;
@@ -79,6 +80,7 @@ interface Props {
7980
onCloseTimelineModal: () => void;
8081
onOpenTimelineModal: () => void;
8182
showTimelineModal: boolean;
83+
status: TimelineStatus;
8284
title: string;
8385
updateNote: UpdateNote;
8486
}
@@ -106,6 +108,7 @@ const PropertiesRightComponent: React.FC<Props> = ({
106108
onCloseTimelineModal,
107109
onOpenTimelineModal,
108110
title,
111+
status,
109112
}) => (
110113
<PropertiesRightStyle alignItems="flexStart" data-test-subj="properties-right" gutterSize="s">
111114
<EuiFlexItem grow={false}>
@@ -142,6 +145,7 @@ const PropertiesRightComponent: React.FC<Props> = ({
142145
onClosePopover={onClosePopover}
143146
timelineId={timelineId}
144147
timelineTitle={title}
148+
timelineStatus={status}
145149
/>
146150
</EuiFlexItem>
147151

x-pack/plugins/siem/public/timelines/containers/one/index.gql_query.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ export const oneTimelineQuery = gql`
128128
updatedBy
129129
version
130130
}
131+
status
131132
title
132133
timelineType
133134
templateTimelineId

0 commit comments

Comments
 (0)