Skip to content

Commit

Permalink
[SIEM] Fix draft timeline can be attached to a case (#67844)
Browse files Browse the repository at this point in the history
# Conflicts:
#	x-pack/plugins/siem/public/timelines/components/flyout/header/index.tsx
#	x-pack/plugins/siem/public/timelines/components/timeline/properties/helpers.tsx
#	x-pack/plugins/siem/public/timelines/components/timeline/properties/index.test.tsx
#	x-pack/plugins/siem/public/timelines/components/timeline/properties/index.tsx
#	x-pack/plugins/siem/public/timelines/components/timeline/properties/properties_right.tsx
  • Loading branch information
patrykkopycinski committed Jun 2, 2020
1 parent a4da8f5 commit 81414fb
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 34 deletions.
4 changes: 4 additions & 0 deletions x-pack/plugins/siem/public/components/flyout/header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const StatefulFlyoutHeader = React.memo<Props>(
title,
noteIds,
notesById,
status,
timelineId,
toggleLock,
updateDescription,
Expand All @@ -68,6 +69,7 @@ const StatefulFlyoutHeader = React.memo<Props>(
isFavorite={isFavorite}
title={title}
noteIds={noteIds}
status={status}
timelineId={timelineId}
toggleLock={toggleLock}
updateDescription={updateDescription}
Expand Down Expand Up @@ -100,6 +102,7 @@ const makeMapStateToProps = () => {
kqlQuery,
title = '',
noteIds = emptyNotesId,
status,
} = timeline;

const history = emptyHistory; // TODO: get history from store via selector
Expand All @@ -113,6 +116,7 @@ const makeMapStateToProps = () => {
isFavorite,
isDatepickerLocked: globalInput.linkTo.includes('timeline'),
noteIds,
status,
title,
};
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import styled from 'styled-components';
import { useHistory } from 'react-router-dom';
import { useSelector } from 'react-redux';

import { TimelineStatus } from '../../../../common/types/timeline';
import { Note } from '../../../lib/note';
import { Notes } from '../../notes';
import { AssociateNote, UpdateNote } from '../../notes/helpers';
Expand Down Expand Up @@ -85,7 +86,7 @@ export const Description = React.memo<DescriptionProps>(
aria-label={i18n.TIMELINE_DESCRIPTION}
data-test-subj="timeline-description"
fullWidth={true}
onChange={(e) => updateDescription({ id: timelineId, description: e.target.value })}
onChange={e => updateDescription({ id: timelineId, description: e.target.value })}
placeholder={i18n.DESCRIPTION}
spellCheck={true}
value={description}
Expand All @@ -107,7 +108,7 @@ export const Name = React.memo<NameProps>(({ timelineId, title, updateTitle }) =
<NameField
aria-label={i18n.TIMELINE_TITLE}
data-test-subj="timeline-title"
onChange={(e) => updateTitle({ id: timelineId, title: e.target.value })}
onChange={e => updateTitle({ id: timelineId, title: e.target.value })}
placeholder={i18n.UNTITLED_TIMELINE}
spellCheck={true}
value={title}
Expand All @@ -119,40 +120,44 @@ Name.displayName = 'Name';
interface NewCaseProps {
onClosePopover: () => void;
timelineId: string;
timelineStatus: TimelineStatus;
timelineTitle: string;
}

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({
pathname: `/${SiemPageName.case}/create`,
state: {
insertTimeline: {
timelineId,
timelineSavedObjectId: savedObjectId,
timelineTitle: timelineTitle.length > 0 ? timelineTitle : i18n.UNTITLED_TIMELINE,
export const NewCase = React.memo<NewCaseProps>(
({ onClosePopover, timelineId, timelineStatus, timelineTitle }) => {
const history = useHistory();
const { savedObjectId } = useSelector((state: State) =>
timelineSelectors.selectTimeline(state, timelineId)
);
const handleClick = useCallback(() => {
onClosePopover();
history.push({
pathname: `/${SiemPageName.case}/create`,
state: {
insertTimeline: {
timelineId,
timelineSavedObjectId: savedObjectId,
timelineTitle: timelineTitle.length > 0 ? timelineTitle : i18n.UNTITLED_TIMELINE,
},
},
},
});
}, [onClosePopover, history, timelineId, timelineTitle]);
});
}, [onClosePopover, history, timelineId, timelineTitle]);

return (
<EuiButtonEmpty
data-test-subj="attach-timeline-case"
color="text"
iconSide="left"
iconType="paperClip"
onClick={handleClick}
>
{i18n.ATTACH_TIMELINE_TO_NEW_CASE}
</EuiButtonEmpty>
);
});
return (
<EuiButtonEmpty
data-test-subj="attach-timeline-case"
color="text"
iconSide="left"
iconType="paperClip"
disabled={timelineStatus === TimelineStatus.draft}
onClick={handleClick}
>
{i18n.ATTACH_TIMELINE_TO_NEW_CASE}
</EuiButtonEmpty>
);
}
);
NewCase.displayName = 'NewCase';

interface NewTimelineProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import { mount } from 'enzyme';
import React from 'react';
import { Provider as ReduxStoreProvider } from 'react-redux';

import { TimelineStatus } from '../../../../common/types/timeline';
import { mockGlobalState, apolloClientObservable } from '../../../mock';
import { createStore, State } from '../../../store';
import { useThrottledResizeObserver } from '../../utils';

import { Properties, showDescriptionThreshold, showNotesThreshold } from '.';

jest.mock('../../../lib/kibana');
Expand All @@ -22,6 +22,24 @@ jest.mock('../../utils');
width: mockedWidth,
}));

jest.mock('react-redux', () => {
const originalModule = jest.requireActual('react-redux');

return {
...originalModule,
useSelector: jest.fn().mockReturnValue({ savedObjectId: '1' }),
};
});

jest.mock('react-router-dom', () => {
const originalModule = jest.requireActual('react-router-dom');

return {
...originalModule,
useHistory: jest.fn(),
};
});

describe('Properties', () => {
const usersViewing = ['elastic'];

Expand All @@ -47,6 +65,7 @@ describe('Properties', () => {
description=""
getNotesByIds={jest.fn()}
noteIds={[]}
status={TimelineStatus.active}
timelineId="abc"
toggleLock={jest.fn()}
updateDescription={jest.fn()}
Expand All @@ -57,7 +76,51 @@ describe('Properties', () => {
/>
</ReduxStoreProvider>
);

wrapper
.find('[data-test-subj="settings-gear"]')
.at(0)
.simulate('click');

expect(wrapper.find('[data-test-subj="timeline-properties"]').exists()).toEqual(true);
expect(wrapper.find('button[data-test-subj="attach-timeline-case"]').prop('disabled')).toEqual(
false
);
});

test('renders correctly draft timeline', () => {
const wrapper = mount(
<ReduxStoreProvider store={store}>
<Properties
associateNote={jest.fn()}
createTimeline={jest.fn()}
isDataInTimeline={false}
isDatepickerLocked={false}
isFavorite={false}
title=""
description=""
getNotesByIds={jest.fn()}
noteIds={[]}
status={TimelineStatus.draft}
timelineId="abc"
toggleLock={jest.fn()}
updateDescription={jest.fn()}
updateIsFavorite={jest.fn()}
updateTitle={jest.fn()}
updateNote={jest.fn()}
usersViewing={usersViewing}
/>
</ReduxStoreProvider>
);

wrapper
.find('[data-test-subj="settings-gear"]')
.at(0)
.simulate('click');

expect(wrapper.find('button[data-test-subj="attach-timeline-case"]').prop('disabled')).toEqual(
true
);
});

test('it renders an empty star icon when it is NOT a favorite', () => {
Expand All @@ -73,6 +136,7 @@ describe('Properties', () => {
description=""
getNotesByIds={jest.fn()}
noteIds={[]}
status={TimelineStatus.active}
timelineId="abc"
toggleLock={jest.fn()}
updateDescription={jest.fn()}
Expand Down Expand Up @@ -100,6 +164,7 @@ describe('Properties', () => {
description=""
getNotesByIds={jest.fn()}
noteIds={[]}
status={TimelineStatus.active}
timelineId="abc"
toggleLock={jest.fn()}
updateDescription={jest.fn()}
Expand Down Expand Up @@ -129,6 +194,7 @@ describe('Properties', () => {
description=""
getNotesByIds={jest.fn()}
noteIds={[]}
status={TimelineStatus.active}
timelineId="abc"
toggleLock={jest.fn()}
updateDescription={jest.fn()}
Expand All @@ -140,7 +206,12 @@ describe('Properties', () => {
</ReduxStoreProvider>
);

expect(wrapper.find('[data-test-subj="timeline-title"]').first().props().value).toEqual(title);
expect(
wrapper
.find('[data-test-subj="timeline-title"]')
.first()
.props().value
).toEqual(title);
});

test('it renders the date picker with the lock icon', () => {
Expand All @@ -156,6 +227,7 @@ describe('Properties', () => {
description=""
getNotesByIds={jest.fn()}
noteIds={[]}
status={TimelineStatus.active}
timelineId="abc"
toggleLock={jest.fn()}
updateDescription={jest.fn()}
Expand Down Expand Up @@ -188,6 +260,7 @@ describe('Properties', () => {
description=""
getNotesByIds={jest.fn()}
noteIds={[]}
status={TimelineStatus.active}
timelineId="abc"
toggleLock={jest.fn()}
updateDescription={jest.fn()}
Expand Down Expand Up @@ -219,6 +292,7 @@ describe('Properties', () => {
description=""
getNotesByIds={jest.fn()}
noteIds={[]}
status={TimelineStatus.active}
timelineId="abc"
toggleLock={jest.fn()}
updateDescription={jest.fn()}
Expand Down Expand Up @@ -253,6 +327,7 @@ describe('Properties', () => {
description={description}
getNotesByIds={jest.fn()}
noteIds={[]}
status={TimelineStatus.active}
timelineId="abc"
toggleLock={jest.fn()}
updateDescription={jest.fn()}
Expand Down Expand Up @@ -289,6 +364,7 @@ describe('Properties', () => {
description={description}
getNotesByIds={jest.fn()}
noteIds={[]}
status={TimelineStatus.active}
timelineId="abc"
toggleLock={jest.fn()}
updateDescription={jest.fn()}
Expand Down Expand Up @@ -323,6 +399,7 @@ describe('Properties', () => {
description=""
getNotesByIds={jest.fn()}
noteIds={[]}
status={TimelineStatus.active}
timelineId="abc"
toggleLock={jest.fn()}
updateDescription={jest.fn()}
Expand Down Expand Up @@ -357,6 +434,7 @@ describe('Properties', () => {
description=""
getNotesByIds={jest.fn()}
noteIds={[]}
status={TimelineStatus.active}
timelineId="abc"
toggleLock={jest.fn()}
updateDescription={jest.fn()}
Expand Down Expand Up @@ -389,6 +467,7 @@ describe('Properties', () => {
description=""
getNotesByIds={jest.fn()}
noteIds={[]}
status={TimelineStatus.active}
timelineId="abc"
toggleLock={jest.fn()}
updateDescription={jest.fn()}
Expand Down Expand Up @@ -418,6 +497,7 @@ describe('Properties', () => {
description=""
getNotesByIds={jest.fn()}
noteIds={[]}
status={TimelineStatus.active}
timelineId="abc"
toggleLock={jest.fn()}
updateDescription={jest.fn()}
Expand Down Expand Up @@ -445,6 +525,7 @@ describe('Properties', () => {
description=""
getNotesByIds={jest.fn()}
noteIds={[]}
status={TimelineStatus.active}
timelineId="abc"
toggleLock={jest.fn()}
updateDescription={jest.fn()}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

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

import { TimelineStatus } from '../../../../common/types/timeline';
import { useThrottledResizeObserver } from '../../utils';
import { Note } from '../../../lib/note';
import { InputsModelId } from '../../../store/inputs/constants';
Expand All @@ -31,6 +32,7 @@ interface Props {
isFavorite: boolean;
noteIds: string[];
timelineId: string;
status: TimelineStatus;
title: string;
toggleLock: ToggleLock;
updateDescription: UpdateDescription;
Expand Down Expand Up @@ -62,6 +64,7 @@ export const Properties = React.memo<Props>(
isDatepickerLocked,
isFavorite,
noteIds,
status,
timelineId,
title,
toggleLock,
Expand Down Expand Up @@ -140,6 +143,7 @@ export const Properties = React.memo<Props>(
showNotesFromWidth={width < showNotesThreshold}
showTimelineModal={showTimelineModal}
showUsersView={title.length > 0}
status={status}
timelineId={timelineId}
title={title}
updateDescription={updateDescription}
Expand Down
Loading

0 comments on commit 81414fb

Please sign in to comment.