mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
(cherry picked from commit b41bc6643d
)
Co-authored-by: Michael Olorunnisola <michael.olorunnisola@elastic.co>
This commit is contained in:
parent
9ee96d7b64
commit
6824d89493
9 changed files with 196 additions and 21 deletions
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import { AddEventNoteAction } from './add_note_icon_item';
|
||||
import { useUserPrivileges } from '../../../../../common/components/user_privileges';
|
||||
import { getEndpointPrivilegesInitialStateMock } from '../../../../../common/components/user_privileges/endpoint/mocks';
|
||||
import { TestProviders } from '../../../../../common/mock';
|
||||
import { TimelineType } from '../../../../../../common/types';
|
||||
|
||||
jest.mock('../../../../../common/components/user_privileges');
|
||||
const useUserPrivilegesMock = useUserPrivileges as jest.Mock;
|
||||
|
||||
describe('AddEventNoteAction', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('isDisabled', () => {
|
||||
test('it disables the add note button when the user does NOT have crud privileges', () => {
|
||||
useUserPrivilegesMock.mockReturnValue({
|
||||
kibanaSecuritySolutionsPrivileges: { crud: false, read: true },
|
||||
endpointPrivileges: getEndpointPrivilegesInitialStateMock(),
|
||||
});
|
||||
|
||||
render(
|
||||
<TestProviders>
|
||||
<AddEventNoteAction
|
||||
showNotes={false}
|
||||
timelineType={TimelineType.default}
|
||||
toggleShowNotes={jest.fn}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('timeline-notes-button-small')).toHaveClass(
|
||||
'euiButtonIcon-isDisabled'
|
||||
);
|
||||
});
|
||||
|
||||
test('it enables the add note button when the user has crud privileges', () => {
|
||||
useUserPrivilegesMock.mockReturnValue({
|
||||
kibanaSecuritySolutionsPrivileges: { crud: true, read: true },
|
||||
endpointPrivileges: getEndpointPrivilegesInitialStateMock(),
|
||||
});
|
||||
|
||||
render(
|
||||
<TestProviders>
|
||||
<AddEventNoteAction
|
||||
showNotes={false}
|
||||
timelineType={TimelineType.default}
|
||||
toggleShowNotes={jest.fn}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('timeline-notes-button-small')).not.toHaveClass(
|
||||
'euiButtonIcon-isDisabled'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -11,6 +11,7 @@ import { TimelineType } from '../../../../../../common/types/timeline';
|
|||
import * as i18n from '../translations';
|
||||
import { NotesButton } from '../../properties/helpers';
|
||||
import { ActionIconItem } from './action_icon_item';
|
||||
import { useUserPrivileges } from '../../../../../common/components/user_privileges';
|
||||
|
||||
interface AddEventNoteActionProps {
|
||||
ariaLabel?: string;
|
||||
|
@ -24,20 +25,25 @@ const AddEventNoteActionComponent: React.FC<AddEventNoteActionProps> = ({
|
|||
showNotes,
|
||||
timelineType,
|
||||
toggleShowNotes,
|
||||
}) => (
|
||||
<ActionIconItem>
|
||||
<NotesButton
|
||||
ariaLabel={ariaLabel}
|
||||
data-test-subj="add-note"
|
||||
showNotes={showNotes}
|
||||
timelineType={timelineType}
|
||||
toggleShowNotes={toggleShowNotes}
|
||||
toolTip={
|
||||
timelineType === TimelineType.template ? i18n.NOTES_DISABLE_TOOLTIP : i18n.NOTES_TOOLTIP
|
||||
}
|
||||
/>
|
||||
</ActionIconItem>
|
||||
);
|
||||
}) => {
|
||||
const { kibanaSecuritySolutionsPrivileges } = useUserPrivileges();
|
||||
|
||||
return (
|
||||
<ActionIconItem>
|
||||
<NotesButton
|
||||
ariaLabel={ariaLabel}
|
||||
data-test-subj="add-note"
|
||||
isDisabled={kibanaSecuritySolutionsPrivileges.crud === false}
|
||||
showNotes={showNotes}
|
||||
timelineType={timelineType}
|
||||
toggleShowNotes={toggleShowNotes}
|
||||
toolTip={
|
||||
timelineType === TimelineType.template ? i18n.NOTES_DISABLE_TOOLTIP : i18n.NOTES_TOOLTIP
|
||||
}
|
||||
/>
|
||||
</ActionIconItem>
|
||||
);
|
||||
};
|
||||
|
||||
AddEventNoteActionComponent.displayName = 'AddEventNoteActionComponent';
|
||||
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import { PinEventAction } from './pin_event_action';
|
||||
import { useUserPrivileges } from '../../../../../common/components/user_privileges';
|
||||
import { getEndpointPrivilegesInitialStateMock } from '../../../../../common/components/user_privileges/endpoint/mocks';
|
||||
import { TestProviders } from '../../../../../common/mock';
|
||||
import { TimelineType } from '../../../../../../common/types';
|
||||
|
||||
jest.mock('../../../../../common/components/user_privileges');
|
||||
const useUserPrivilegesMock = useUserPrivileges as jest.Mock;
|
||||
|
||||
describe('PinEventAction', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('isDisabled', () => {
|
||||
test('it disables the pin event button when the user does NOT have crud privileges', () => {
|
||||
useUserPrivilegesMock.mockReturnValue({
|
||||
kibanaSecuritySolutionsPrivileges: { crud: false, read: true },
|
||||
endpointPrivileges: getEndpointPrivilegesInitialStateMock(),
|
||||
});
|
||||
|
||||
render(
|
||||
<TestProviders>
|
||||
<PinEventAction
|
||||
isAlert={false}
|
||||
noteIds={[]}
|
||||
onPinClicked={jest.fn}
|
||||
eventIsPinned={false}
|
||||
timelineType={TimelineType.default}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('pin')).toHaveClass('euiButtonIcon-isDisabled');
|
||||
});
|
||||
|
||||
test('it enables the pin event button when the user has crud privileges', () => {
|
||||
useUserPrivilegesMock.mockReturnValue({
|
||||
kibanaSecuritySolutionsPrivileges: { crud: true, read: true },
|
||||
endpointPrivileges: getEndpointPrivilegesInitialStateMock(),
|
||||
});
|
||||
|
||||
render(
|
||||
<TestProviders>
|
||||
<PinEventAction
|
||||
isAlert={false}
|
||||
noteIds={[]}
|
||||
onPinClicked={jest.fn}
|
||||
eventIsPinned={false}
|
||||
timelineType={TimelineType.default}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('pin')).not.toHaveClass('euiButtonIcon-isDisabled');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -13,6 +13,7 @@ import { EventsTdContent } from '../../styles';
|
|||
import { eventHasNotes, getPinTooltip } from '../helpers';
|
||||
import { Pin } from '../../pin';
|
||||
import { TimelineType } from '../../../../../../common/types/timeline';
|
||||
import { useUserPrivileges } from '../../../../../common/components/user_privileges';
|
||||
|
||||
interface PinEventActionProps {
|
||||
ariaLabel?: string;
|
||||
|
@ -31,6 +32,7 @@ const PinEventActionComponent: React.FC<PinEventActionProps> = ({
|
|||
eventIsPinned,
|
||||
timelineType,
|
||||
}) => {
|
||||
const { kibanaSecuritySolutionsPrivileges } = useUserPrivileges();
|
||||
const tooltipContent = useMemo(
|
||||
() =>
|
||||
getPinTooltip({
|
||||
|
@ -50,6 +52,7 @@ const PinEventActionComponent: React.FC<PinEventActionProps> = ({
|
|||
ariaLabel={ariaLabel}
|
||||
allowUnpinning={!eventHasNotes(noteIds)}
|
||||
data-test-subj="pin-event"
|
||||
isDisabled={kibanaSecuritySolutionsPrivileges.crud === false}
|
||||
isAlert={isAlert}
|
||||
onClick={onPinClicked}
|
||||
pinned={eventIsPinned}
|
||||
|
|
|
@ -28,6 +28,17 @@ jest.mock('../../../../../common/hooks/use_selector', () => ({
|
|||
useShallowEqualSelector: jest.fn(),
|
||||
useDeepEqualSelector: jest.fn(),
|
||||
}));
|
||||
jest.mock('../../../../../common/components/user_privileges', () => {
|
||||
return {
|
||||
useUserPrivileges: () => ({
|
||||
listPrivileges: { loading: false, error: undefined, result: undefined },
|
||||
detectionEnginePrivileges: { loading: false, error: undefined, result: undefined },
|
||||
endpointPrivileges: {},
|
||||
kibanaSecuritySolutionsPrivileges: { crud: true, read: true },
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../../../common/lib/kibana', () => ({
|
||||
useKibana: () => ({
|
||||
services: {
|
||||
|
|
|
@ -36,6 +36,17 @@ import { createStore, State } from '../../../../common/store';
|
|||
|
||||
jest.mock('../../../../common/lib/kibana/hooks');
|
||||
jest.mock('../../../../common/hooks/use_app_toasts');
|
||||
jest.mock('../../../../common/components/user_privileges', () => {
|
||||
return {
|
||||
useUserPrivileges: () => ({
|
||||
listPrivileges: { loading: false, error: undefined, result: undefined },
|
||||
detectionEnginePrivileges: { loading: false, error: undefined, result: undefined },
|
||||
endpointPrivileges: {},
|
||||
kibanaSecuritySolutionsPrivileges: { crud: true, read: true },
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../../common/lib/kibana', () => {
|
||||
const originalModule = jest.requireActual('../../../../common/lib/kibana');
|
||||
const mockCasesContract = jest.requireActual('@kbn/cases-plugin/public/mocks');
|
||||
|
@ -225,7 +236,7 @@ describe('Body', () => {
|
|||
mockDispatch.mockClear();
|
||||
});
|
||||
|
||||
test('Add a Note to an event', () => {
|
||||
test('Add a note to an event', () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<StatefulBody {...props} />
|
||||
|
@ -257,7 +268,7 @@ describe('Body', () => {
|
|||
);
|
||||
});
|
||||
|
||||
test('Add two Note to an event', () => {
|
||||
test('Add two notes to an event', () => {
|
||||
const { storage } = createSecuritySolutionStorageMock();
|
||||
const state: State = {
|
||||
...mockGlobalState,
|
||||
|
|
|
@ -39,6 +39,7 @@ import { getTimelineNoteSelector } from './selectors';
|
|||
import { DetailsPanel } from '../../side_panel';
|
||||
import { getScrollToTopSelector } from '../tabs_content/selectors';
|
||||
import { useScrollToTop } from '../../../../common/components/scroll_to_top';
|
||||
import { useUserPrivileges } from '../../../../common/components/user_privileges';
|
||||
|
||||
const FullWidthFlexGroup = styled(EuiFlexGroup)`
|
||||
width: 100%;
|
||||
|
@ -131,6 +132,7 @@ interface NotesTabContentProps {
|
|||
|
||||
const NotesTabContentComponent: React.FC<NotesTabContentProps> = ({ timelineId }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { kibanaSecuritySolutionsPrivileges } = useUserPrivileges();
|
||||
|
||||
const getScrollToTop = useMemo(() => getScrollToTopSelector(), []);
|
||||
const scrollToTop = useShallowEqualSelector((state) => getScrollToTop(state, timelineId));
|
||||
|
@ -239,7 +241,7 @@ const NotesTabContentComponent: React.FC<NotesTabContentProps> = ({ timelineId }
|
|||
showTimelineDescription
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
{!isImmutable && (
|
||||
{!isImmutable && kibanaSecuritySolutionsPrivileges.crud === true && (
|
||||
<AddNote
|
||||
associateNote={associateNote}
|
||||
newNote={newNote}
|
||||
|
|
|
@ -21,6 +21,7 @@ interface Props {
|
|||
ariaLabel?: string;
|
||||
allowUnpinning: boolean;
|
||||
isAlert: boolean;
|
||||
isDisabled?: boolean;
|
||||
timelineType?: TimelineTypeLiteral;
|
||||
onClick?: () => void;
|
||||
pinned: boolean;
|
||||
|
@ -45,7 +46,7 @@ export const getDefaultAriaLabel = ({
|
|||
};
|
||||
|
||||
export const Pin = React.memo<Props>(
|
||||
({ ariaLabel, allowUnpinning, isAlert, onClick = noop, pinned, timelineType }) => {
|
||||
({ ariaLabel, allowUnpinning, isAlert, isDisabled, onClick = noop, pinned, timelineType }) => {
|
||||
const isTemplate = timelineType === TimelineType.template;
|
||||
const defaultAriaLabel = getDefaultAriaLabel({
|
||||
isAlert,
|
||||
|
@ -60,7 +61,7 @@ export const Pin = React.memo<Props>(
|
|||
data-test-subj="pin"
|
||||
iconType={getPinIcon(pinned)}
|
||||
onClick={onClick}
|
||||
isDisabled={isTemplate || !allowUnpinning}
|
||||
isDisabled={isDisabled || isTemplate || !allowUnpinning}
|
||||
size="s"
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -91,6 +91,7 @@ NewTimeline.displayName = 'NewTimeline';
|
|||
|
||||
interface NotesButtonProps {
|
||||
ariaLabel?: string;
|
||||
isDisabled?: boolean;
|
||||
showNotes: boolean;
|
||||
toggleShowNotes: () => void;
|
||||
toolTip?: string;
|
||||
|
@ -99,6 +100,7 @@ interface NotesButtonProps {
|
|||
|
||||
interface SmallNotesButtonProps {
|
||||
ariaLabel?: string;
|
||||
isDisabled?: boolean;
|
||||
toggleShowNotes: () => void;
|
||||
timelineType: TimelineTypeLiteral;
|
||||
}
|
||||
|
@ -106,7 +108,7 @@ interface SmallNotesButtonProps {
|
|||
export const NOTES_BUTTON_CLASS_NAME = 'notes-button';
|
||||
|
||||
const SmallNotesButton = React.memo<SmallNotesButtonProps>(
|
||||
({ ariaLabel = i18n.NOTES, toggleShowNotes, timelineType }) => {
|
||||
({ ariaLabel = i18n.NOTES, isDisabled, toggleShowNotes, timelineType }) => {
|
||||
const isTemplate = timelineType === TimelineType.template;
|
||||
|
||||
return (
|
||||
|
@ -114,6 +116,7 @@ const SmallNotesButton = React.memo<SmallNotesButtonProps>(
|
|||
aria-label={ariaLabel}
|
||||
className={NOTES_BUTTON_CLASS_NAME}
|
||||
data-test-subj="timeline-notes-button-small"
|
||||
disabled={isDisabled}
|
||||
iconType="editorComment"
|
||||
onClick={toggleShowNotes}
|
||||
size="s"
|
||||
|
@ -125,10 +128,11 @@ const SmallNotesButton = React.memo<SmallNotesButtonProps>(
|
|||
SmallNotesButton.displayName = 'SmallNotesButton';
|
||||
|
||||
export const NotesButton = React.memo<NotesButtonProps>(
|
||||
({ ariaLabel, showNotes, timelineType, toggleShowNotes, toolTip }) =>
|
||||
({ ariaLabel, isDisabled, showNotes, timelineType, toggleShowNotes, toolTip }) =>
|
||||
showNotes ? (
|
||||
<SmallNotesButton
|
||||
ariaLabel={ariaLabel}
|
||||
isDisabled={isDisabled}
|
||||
toggleShowNotes={toggleShowNotes}
|
||||
timelineType={timelineType}
|
||||
/>
|
||||
|
@ -136,6 +140,7 @@ export const NotesButton = React.memo<NotesButtonProps>(
|
|||
<EuiToolTip content={toolTip || ''} data-test-subj="timeline-notes-tool-tip">
|
||||
<SmallNotesButton
|
||||
ariaLabel={ariaLabel}
|
||||
isDisabled={isDisabled}
|
||||
toggleShowNotes={toggleShowNotes}
|
||||
timelineType={timelineType}
|
||||
/>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue