Skip to content

Commit

Permalink
[SecuritySolution] Rename timeline-saving-related components (elastic…
Browse files Browse the repository at this point in the history
…#166740)

## Summary

This PR prepares further work on the timeline-saving-related components.
As a first step we're only renaming the components to make it easier to
reason about their function.

> `SaveTimelineButton` -> `EditTimelineButton`:

We might have a dedicated `save` button more prominently in the UI in
the near future. The former "save" button actually opened up a modal
with the `edit` form. In cypress tests, the component was already
referred to as the `edit` button.

> `TimelineTitleAndDescription` -> `EditTimelineModal`:

The original name did not make it clear that it's actually a form in a
modal.

### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
  • Loading branch information
janmonschke authored Sep 21, 2023
1 parent d7369d9 commit 1e3f438
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 121 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { AddToFavoritesButton } from '../../timeline/properties/helpers';
import type { TimerangeInput } from '../../../../../common/search_strategy';
import { AddToCaseButton } from '../add_to_case_button';
import { AddTimelineButton } from '../add_timeline_button';
import { SaveTimelineButton } from '../../timeline/header/save_timeline_button';
import { EditTimelineButton } from '../../timeline/header/edit_timeline_button';
import { useGetUserCasesPermissions, useKibana } from '../../../../common/lib/kibana';
import { InspectButton } from '../../../../common/components/inspect';
import { useTimelineKpis } from '../../../containers/kpis';
Expand Down Expand Up @@ -421,14 +421,14 @@ const FlyoutHeaderComponent: React.FC<FlyoutHeaderProps> = ({ timelineId }) => {
<EuiFlexGroup data-test-subj="properties-left" direction="column" gutterSize="none">
<RowFlexItem>
<TimelineName timelineId={timelineId} />
<SaveTimelineButton timelineId={timelineId} initialFocus="title" />
<EditTimelineButton timelineId={timelineId} initialFocus="title" />
<TimelineStatusInfoContainer>
<TimelineStatusInfo timelineId={timelineId} />
</TimelineStatusInfoContainer>
</RowFlexItem>
<RowFlexItem>
<TimelineDescription timelineId={timelineId} />
<SaveTimelineButton timelineId={timelineId} initialFocus="description" />
<EditTimelineButton timelineId={timelineId} initialFocus="description" />
</RowFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { NOTE_CONTENT_CLASS_NAME } from '../../timeline/body/helpers';
import * as i18n from './translations';
import { TimelineTabs } from '../../../../../common/types/timeline';
import { useDeepEqualSelector } from '../../../../common/hooks/use_selector';
import { SaveTimelineButton } from '../../timeline/header/save_timeline_button';
import { EditTimelineButton } from '../../timeline/header/edit_timeline_button';
import { SourcererScopeName } from '../../../../common/store/sourcerer/model';
import { useSourcererDataView } from '../../../../common/containers/sourcerer';
import { useKibana } from '../../../../common/lib/kibana';
Expand Down Expand Up @@ -226,7 +226,7 @@ export const NotePreviews = React.memo<NotePreviewsProps>(
size="l"
/>
),
actions: <SaveTimelineButton timelineId={timelineId} initialFocus="description" />,
actions: <EditTimelineButton timelineId={timelineId} initialFocus="description" />,
},
]
: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@

import React from 'react';
import { render, fireEvent, waitFor, screen } from '@testing-library/react';
import type { SaveTimelineComponentProps } from './save_timeline_button';
import { SaveTimelineButton } from './save_timeline_button';
import type { EditTimelineComponentProps } from './edit_timeline_button';
import { EditTimelineButton } from './edit_timeline_button';
import { TestProviders } from '../../../../common/mock';
import { useUserPrivileges } from '../../../../common/components/user_privileges';

const TEST_ID = {
SAVE_TIMELINE_MODAL: 'save-timeline-modal',
SAVE_TIMELINE_BTN: 'save-timeline-button-icon',
SAVE_TIMELINE_TOOLTIP: 'save-timeline-tooltip',
EDIT_TIMELINE_MODAL: 'edit-timeline-modal',
EDIT_TIMELINE_BTN: 'edit-timeline-button-icon',
EDIT_TIMELINE_TOOLTIP: 'edit-timeline-tooltip',
};

jest.mock('react-redux', () => {
Expand All @@ -36,41 +36,36 @@ const props = {
toolTip: 'tooltip message',
};

const TestSaveTimelineButton = (_props: SaveTimelineComponentProps) => (
const TestEditTimelineButton = (_props: EditTimelineComponentProps) => (
<TestProviders>
<SaveTimelineButton {..._props} />
<EditTimelineButton {..._props} />
</TestProviders>
);

jest.mock('raf', () => {
return jest.fn().mockImplementation((cb) => cb());
});

describe('SaveTimelineButton', () => {
describe('EditTimelineButton', () => {
beforeEach(() => {
jest.clearAllMocks();
});

// skipping this test because popover is not getting visible by RTL gestures.
//
// Raised a bug with eui team: https://github.com/elastic/eui/issues/6065
xit('Show tooltip', async () => {
render(<TestSaveTimelineButton {...props} />);
const saveTimelineIcon = screen.queryAllByTestId(TEST_ID.SAVE_TIMELINE_BTN)[0];
it('Show tooltip', async () => {
render(<TestEditTimelineButton {...props} />);
const editTimelineIcon = screen.queryAllByTestId(TEST_ID.EDIT_TIMELINE_BTN)[0];

fireEvent.mouseOver(saveTimelineIcon);

jest.runAllTimers();
fireEvent.mouseOver(editTimelineIcon);

await waitFor(() => {
expect(screen.getByRole('tooltip')).toBeVisible();
});
});

it('should show a button with pencil icon', () => {
render(<TestSaveTimelineButton {...props} />);
render(<TestEditTimelineButton {...props} />);

expect(screen.getByTestId(TEST_ID.SAVE_TIMELINE_BTN).firstChild).toHaveAttribute(
expect(screen.getByTestId(TEST_ID.EDIT_TIMELINE_BTN).firstChild).toHaveAttribute(
'data-euiicon-type',
'pencil'
);
Expand All @@ -82,43 +77,43 @@ describe('SaveTimelineButton', () => {
});
render(
<TestProviders>
<SaveTimelineButton {...props} />
<EditTimelineButton {...props} />
</TestProviders>
);
expect(screen.getByTestId(TEST_ID.SAVE_TIMELINE_BTN)).toBeDisabled();
expect(screen.getByTestId(TEST_ID.EDIT_TIMELINE_BTN)).toBeDisabled();
});

it('should not show modal if user does not have write access', async () => {
(useUserPrivileges as jest.Mock).mockReturnValue({
kibanaSecuritySolutionsPrivileges: { crud: false },
});
render(<TestSaveTimelineButton {...props} />);
render(<TestEditTimelineButton {...props} />);

expect(screen.queryByTestId(TEST_ID.SAVE_TIMELINE_MODAL)).not.toBeInTheDocument();
expect(screen.queryByTestId(TEST_ID.EDIT_TIMELINE_MODAL)).not.toBeInTheDocument();

const saveTimelineIcon = screen.getByTestId(TEST_ID.SAVE_TIMELINE_BTN);
const editTimelineIcon = screen.getByTestId(TEST_ID.EDIT_TIMELINE_BTN);

fireEvent.click(saveTimelineIcon);
fireEvent.click(editTimelineIcon);

await waitFor(() => {
expect(screen.queryAllByTestId(TEST_ID.SAVE_TIMELINE_MODAL)).toHaveLength(0);
expect(screen.queryAllByTestId(TEST_ID.EDIT_TIMELINE_MODAL)).toHaveLength(0);
});
});

it('should show a modal when user has crud privileges', async () => {
(useUserPrivileges as jest.Mock).mockReturnValue({
kibanaSecuritySolutionsPrivileges: { crud: true },
});
render(<TestSaveTimelineButton {...props} />);
expect(screen.queryByTestId(TEST_ID.SAVE_TIMELINE_MODAL)).not.toBeInTheDocument();
render(<TestEditTimelineButton {...props} />);
expect(screen.queryByTestId(TEST_ID.EDIT_TIMELINE_MODAL)).not.toBeInTheDocument();

const saveTimelineIcon = screen.queryAllByTestId(TEST_ID.SAVE_TIMELINE_BTN)[0];
const editTimelineIcon = screen.queryAllByTestId(TEST_ID.EDIT_TIMELINE_BTN)[0];

fireEvent.click(saveTimelineIcon);
fireEvent.click(editTimelineIcon);

await waitFor(() => {
expect(screen.queryByTestId(TEST_ID.SAVE_TIMELINE_TOOLTIP)).not.toBeInTheDocument();
expect(screen.queryAllByTestId(TEST_ID.SAVE_TIMELINE_MODAL)[0]).toBeVisible();
expect(screen.queryByTestId(TEST_ID.EDIT_TIMELINE_TOOLTIP)).not.toBeInTheDocument();
expect(screen.queryAllByTestId(TEST_ID.EDIT_TIMELINE_MODAL)[0]).toBeVisible();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,24 @@ import { TimelineId } from '../../../../../common/types/timeline';
import { useDeepEqualSelector } from '../../../../common/hooks/use_selector';
import { timelineActions } from '../../../store/timeline';
import { getTimelineSaveModalByIdSelector } from './selectors';
import { TimelineTitleAndDescription } from './title_and_description';
import { EditTimelineModal } from './edit_timeline_modal';
import * as timelineTranslations from './translations';

export interface SaveTimelineComponentProps {
export interface EditTimelineComponentProps {
initialFocus: 'title' | 'description';
timelineId: string;
toolTip?: string;
}

export const SaveTimelineButton = React.memo<SaveTimelineComponentProps>(
export const EditTimelineButton = React.memo<EditTimelineComponentProps>(
({ initialFocus, timelineId, toolTip }) => {
const dispatch = useDispatch();
const getTimelineSaveModal = useMemo(() => getTimelineSaveModalByIdSelector(), []);
const show = useDeepEqualSelector((state) => getTimelineSaveModal(state, timelineId));
const [showSaveTimelineOverlay, setShowSaveTimelineOverlay] = useState<boolean>(false);
const [showEditTimelineOverlay, setShowEditTimelineOverlay] = useState<boolean>(false);

const closeSaveTimeline = useCallback(() => {
setShowSaveTimelineOverlay(false);
const closeEditTimeline = useCallback(() => {
setShowEditTimelineOverlay(false);
if (show) {
dispatch(
timelineActions.toggleModalSaveTimeline({
Expand All @@ -40,11 +40,11 @@ export const SaveTimelineButton = React.memo<SaveTimelineComponentProps>(
})
);
}
}, [dispatch, setShowSaveTimelineOverlay, show]);
}, [dispatch, setShowEditTimelineOverlay, show]);

const openSaveTimeline = useCallback(() => {
setShowSaveTimelineOverlay(true);
}, [setShowSaveTimelineOverlay]);
const openEditTimeline = useCallback(() => {
setShowEditTimelineOverlay(true);
}, [setShowEditTimelineOverlay]);

// Case: 1
// check if user has crud privileges so that user can be allowed to edit the timeline
Expand All @@ -60,35 +60,35 @@ export const SaveTimelineButton = React.memo<SaveTimelineComponentProps>(
[toolTip, hasKibanaCrud]
);

const saveTimelineButtonIcon = useMemo(
const editTimelineButtonIcon = useMemo(
() => (
<EuiButtonIcon
aria-label={timelineTranslations.EDIT}
isDisabled={!hasKibanaCrud}
onClick={openSaveTimeline}
onClick={openEditTimeline}
iconType="pencil"
data-test-subj="save-timeline-button-icon"
data-test-subj="edit-timeline-button-icon"
/>
),
[openSaveTimeline, hasKibanaCrud]
[openEditTimeline, hasKibanaCrud]
);

return (initialFocus === 'title' && show) || showSaveTimelineOverlay ? (
return (initialFocus === 'title' && show) || showEditTimelineOverlay ? (
<>
{saveTimelineButtonIcon}
<TimelineTitleAndDescription
closeSaveTimeline={closeSaveTimeline}
{editTimelineButtonIcon}
<EditTimelineModal
closeEditTimeline={closeEditTimeline}
initialFocus={initialFocus}
timelineId={timelineId}
showWarning={initialFocus === 'title' && show}
/>
</>
) : (
<EuiToolTip content={finalTooltipMsg ?? ''} data-test-subj="save-timeline-btn-tooltip">
{saveTimelineButtonIcon}
<EuiToolTip content={finalTooltipMsg ?? ''} data-test-subj="edit-timeline-btn-tooltip">
{editTimelineButtonIcon}
</EuiToolTip>
);
}
);

SaveTimelineButton.displayName = 'SaveTimelineButton';
EditTimelineButton.displayName = 'EditTimelineButton';
Loading

0 comments on commit 1e3f438

Please sign in to comment.