Skip to content

Commit

Permalink
[Security Solution][Notes] - fix createdBy filter for notes managemen…
Browse files Browse the repository at this point in the history
…t page (elastic#197706)

(cherry picked from commit 1065bbf)
  • Loading branch information
PhilippeOberti committed Oct 28, 2024
1 parent c735fcf commit 641ff0e
Show file tree
Hide file tree
Showing 23 changed files with 174 additions and 85 deletions.
2 changes: 1 addition & 1 deletion oas_docs/output/kibana.serverless.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14812,7 +14812,7 @@ paths:
nullable: true
type: string
- in: query
name: userFilter
name: createdByFilter
schema:
nullable: true
type: string
Expand Down
2 changes: 1 addition & 1 deletion oas_docs/output/kibana.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18242,7 +18242,7 @@ paths:
nullable: true
type: string
- in: query
name: userFilter
name: createdByFilter
schema:
nullable: true
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ export const ALERT_SUPPRESSION_RULE_DETAILS = i18n.translate(
);

export const UPGRADE_NOTES_MANAGEMENT_USER_FILTER = (requiredLicense: string) =>
i18n.translate('securitySolutionPackages.noteManagement.userFilter.upsell', {
defaultMessage: 'Upgrade to {requiredLicense} to make use of user filters',
i18n.translate('securitySolutionPackages.noteManagement.createdByFilter.upsell', {
defaultMessage: 'Upgrade to {requiredLicense} to make use of createdBy filter',
values: {
requiredLicense,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const GetNotesRequestQuery = z.object({
sortField: z.string().nullable().optional(),
sortOrder: z.string().nullable().optional(),
filter: z.string().nullable().optional(),
userFilter: z.string().nullable().optional(),
createdByFilter: z.string().nullable().optional(),
associatedFilter: AssociatedFilterType.optional(),
});
export type GetNotesRequestQueryInput = z.input<typeof GetNotesRequestQuery>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ paths:
type: string
nullable: true
- in: query
name: userFilter
name: createdByFilter
schema:
nullable: true
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ paths:
nullable: true
type: string
- in: query
name: userFilter
name: createdByFilter
schema:
nullable: true
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ paths:
nullable: true
type: string
- in: query
name: userFilter
name: createdByFilter
schema:
nullable: true
type: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ export const mockGlobalState: State = {
direction: 'desc' as const,
},
filter: '',
userFilter: '',
createdByFilter: '',
associatedFilter: AssociatedFilter.all,
search: '',
selectedIds: [],
Expand Down
6 changes: 3 additions & 3 deletions x-pack/plugins/security_solution/public/notes/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const fetchNotes = async ({
sortField,
sortOrder,
filter,
userFilter,
createdByFilter,
associatedFilter,
search,
}: {
Expand All @@ -52,7 +52,7 @@ export const fetchNotes = async ({
sortField: string;
sortOrder: string;
filter: string;
userFilter: string;
createdByFilter: string;
associatedFilter: AssociatedFilter;
search: string;
}) => {
Expand All @@ -63,7 +63,7 @@ export const fetchNotes = async ({
sortField,
sortOrder,
filter,
userFilter,
createdByFilter,
associatedFilter,
search,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

import { fireEvent, render, screen } from '@testing-library/react';
import React from 'react';
import { UserFilterDropdown } from './user_filter_dropdown';
import { USER_SELECT_TEST_ID } from './test_ids';
import { CreatedByFilterDropdown } from './created_by_filter_dropdown';
import { CREATED_BY_SELECT_TEST_ID } from './test_ids';
import { useSuggestUsers } from '../../common/components/user_profiles/use_suggest_users';
import { useLicense } from '../../common/hooks/use_license';
import { useUpsellingMessage } from '../../common/hooks/use_upselling';
Expand All @@ -32,16 +32,25 @@ describe('UserFilterDropdown', () => {
jest.clearAllMocks();
(useSuggestUsers as jest.Mock).mockReturnValue({
isLoading: false,
data: [{ user: { username: 'test' } }, { user: { username: 'elastic' } }],
data: [
{
uid: '1',
user: { username: 'test' },
},
{
uid: '2',
user: { username: 'elastic' },
},
],
});
(useLicense as jest.Mock).mockReturnValue({ isPlatinumPlus: () => true });
(useUpsellingMessage as jest.Mock).mockReturnValue('upsellingMessage');
});

it('should render the component enabled', () => {
const { getByTestId } = render(<UserFilterDropdown />);
const { getByTestId } = render(<CreatedByFilterDropdown />);

const dropdown = getByTestId(USER_SELECT_TEST_ID);
const dropdown = getByTestId(CREATED_BY_SELECT_TEST_ID);

expect(dropdown).toBeInTheDocument();
expect(dropdown).not.toHaveClass('euiComboBox-isDisabled');
Expand All @@ -50,13 +59,13 @@ describe('UserFilterDropdown', () => {
it('should render the dropdown disabled', async () => {
(useLicense as jest.Mock).mockReturnValue({ isPlatinumPlus: () => false });

const { getByTestId } = render(<UserFilterDropdown />);
const { getByTestId } = render(<CreatedByFilterDropdown />);

expect(getByTestId(USER_SELECT_TEST_ID)).toHaveClass('euiComboBox-isDisabled');
expect(getByTestId(CREATED_BY_SELECT_TEST_ID)).toHaveClass('euiComboBox-isDisabled');
});

it('should call the correct action when select a user', async () => {
const { getByTestId } = render(<UserFilterDropdown />);
const { getByTestId } = render(<CreatedByFilterDropdown />);

const userSelect = getByTestId('comboBoxSearchInput');
userSelect.focus();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,26 @@ import { i18n } from '@kbn/i18n';
import type { EuiComboBoxOptionOption } from '@elastic/eui/src/components/combo_box/types';
import { useLicense } from '../../common/hooks/use_license';
import { useUpsellingMessage } from '../../common/hooks/use_upselling';
import { USER_SELECT_TEST_ID } from './test_ids';
import { CREATED_BY_SELECT_TEST_ID } from './test_ids';
import { useSuggestUsers } from '../../common/components/user_profiles/use_suggest_users';
import { userFilterUsers } from '..';
import { userFilterCreatedBy } from '..';

export const USERS_DROPDOWN = i18n.translate('xpack.securitySolution.notes.usersDropdownLabel', {
defaultMessage: 'Users',
export const CREATED_BY = i18n.translate('xpack.securitySolution.notes.createdByDropdownLabel', {
defaultMessage: 'Created by',
});

export const UserFilterDropdown = React.memo(() => {
interface User {
/**
* uuid of the UserProfile
*/
id: string;
/**
* full_name || email || username of the UserProfile
*/
label: string;
}

export const CreatedByFilterDropdown = React.memo(() => {
const dispatch = useDispatch();
const isPlatinumPlus = useLicense().isPlatinumPlus();
const upsellingMessage = useUpsellingMessage('note_management_user_filter');
Expand All @@ -30,34 +41,36 @@ export const UserFilterDropdown = React.memo(() => {
searchTerm: '',
enabled: isPlatinumPlus,
});
const users = useMemo(

const users: User[] = useMemo(
() =>
(data || []).map((userProfile: UserProfileWithAvatar) => ({
label: userProfile.user.full_name || userProfile.user.username,
id: userProfile.uid,
label: userProfile.user.full_name || userProfile.user.email || userProfile.user.username,
})),
[data]
);

const [selectedUser, setSelectedUser] = useState<Array<EuiComboBoxOptionOption<string>>>();
const [selectedUser, setSelectedUser] = useState<Array<EuiComboBoxOptionOption<User>>>();
const onChange = useCallback(
(user: Array<EuiComboBoxOptionOption<string>>) => {
(user: Array<EuiComboBoxOptionOption<User>>) => {
setSelectedUser(user);
dispatch(userFilterUsers(user.length > 0 ? user[0].label : ''));
dispatch(userFilterCreatedBy(user.length > 0 ? (user[0].id as string) : ''));
},
[dispatch]
);

const dropdown = useMemo(
() => (
<EuiComboBox
prepend={USERS_DROPDOWN}
prepend={CREATED_BY}
singleSelection={{ asPlainText: true }}
options={users}
selectedOptions={selectedUser}
onChange={onChange}
isLoading={isPlatinumPlus && isLoading}
isDisabled={!isPlatinumPlus}
data-test-subj={USER_SELECT_TEST_ID}
data-test-subj={CREATED_BY_SELECT_TEST_ID}
/>
),
[isLoading, isPlatinumPlus, onChange, selectedUser, users]
Expand All @@ -76,4 +89,4 @@ export const UserFilterDropdown = React.memo(() => {
);
});

UserFilterDropdown.displayName = 'UserFilterDropdown';
CreatedByFilterDropdown.displayName = 'CreatedByFilterDropdown';
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { SearchRow } from './search_row';
import { ASSOCIATED_NOT_SELECT_TEST_ID, SEARCH_BAR_TEST_ID, USER_SELECT_TEST_ID } from './test_ids';
import {
ASSOCIATED_NOT_SELECT_TEST_ID,
SEARCH_BAR_TEST_ID,
CREATED_BY_SELECT_TEST_ID,
} from './test_ids';
import { AssociatedFilter } from '../../../common/notes/constants';
import { useSuggestUsers } from '../../common/components/user_profiles/use_suggest_users';
import { TestProviders } from '../../common/mock';
Expand Down Expand Up @@ -43,7 +47,7 @@ describe('SearchRow', () => {
);

expect(getByTestId(SEARCH_BAR_TEST_ID)).toBeInTheDocument();
expect(getByTestId(USER_SELECT_TEST_ID)).toBeInTheDocument();
expect(getByTestId(CREATED_BY_SELECT_TEST_ID)).toBeInTheDocument();
expect(getByTestId(ASSOCIATED_NOT_SELECT_TEST_ID)).toBeInTheDocument();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
} from '@elastic/eui';
import { useDispatch } from 'react-redux';
import { i18n } from '@kbn/i18n';
import { UserFilterDropdown } from './user_filter_dropdown';
import { CreatedByFilterDropdown } from './created_by_filter_dropdown';
import { ASSOCIATED_NOT_SELECT_TEST_ID, SEARCH_BAR_TEST_ID } from './test_ids';
import { userFilterAssociatedNotes, userSearchedNotes } from '..';
import { AssociatedFilter } from '../../../common/notes/constants';
Expand Down Expand Up @@ -65,7 +65,7 @@ export const SearchRow = React.memo(() => {
<EuiSearchBar box={searchBox} onChange={onQueryChange} defaultQuery="" />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<UserFilterDropdown />
<CreatedByFilterDropdown />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiSelect
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ export const TIMELINE_DESCRIPTION_COMMENT_TEST_ID = `${PREFIX}TimelineDescriptio
export const NOTE_CONTENT_BUTTON_TEST_ID = `${PREFIX}NoteContentButton` as const;
export const NOTE_CONTENT_POPOVER_TEST_ID = `${PREFIX}NoteContentPopover` as const;
export const SEARCH_BAR_TEST_ID = `${PREFIX}SearchBar` as const;
export const USER_SELECT_TEST_ID = `${PREFIX}UserSelect` as const;
export const CREATED_BY_SELECT_TEST_ID = `${PREFIX}CreatedBySelect` as const;
export const ASSOCIATED_NOT_SELECT_TEST_ID = `${PREFIX}AssociatedNoteSelect` as const;
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
selectNotesTableSelectedIds,
selectNotesTableSearch,
userSelectedBulkDelete,
selectNotesTableUserFilters,
selectNotesTableCreatedByFilter,
selectNotesTableAssociatedFilter,
} from '..';

Expand Down Expand Up @@ -53,8 +53,8 @@ export const NotesUtilityBar = React.memo(() => {
const pagination = useSelector(selectNotesPagination);
const sort = useSelector(selectNotesTableSort);
const selectedItems = useSelector(selectNotesTableSelectedIds);
const notesUserFilters = useSelector(selectNotesTableUserFilters);
const notesAssociatedFilters = useSelector(selectNotesTableAssociatedFilter);
const notesCreatedByFilter = useSelector(selectNotesTableCreatedByFilter);
const notesAssociatedFilter = useSelector(selectNotesTableAssociatedFilter);
const resultsCount = useMemo(() => {
const { perPage, page, total } = pagination;
const startOfCurrentPage = perPage * (page - 1) + 1;
Expand Down Expand Up @@ -87,8 +87,8 @@ export const NotesUtilityBar = React.memo(() => {
sortField: sort.field,
sortOrder: sort.direction,
filter: '',
userFilter: notesUserFilters,
associatedFilter: notesAssociatedFilters,
createdByFilter: notesCreatedByFilter,
associatedFilter: notesAssociatedFilter,
search: notesSearch,
})
);
Expand All @@ -98,8 +98,8 @@ export const NotesUtilityBar = React.memo(() => {
pagination.perPage,
sort.field,
sort.direction,
notesUserFilters,
notesAssociatedFilters,
notesCreatedByFilter,
notesAssociatedFilter,
notesSearch,
]);
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
selectNotesTablePendingDeleteIds,
selectFetchNotesError,
ReqStatus,
selectNotesTableUserFilters,
selectNotesTableCreatedByFilter,
selectNotesTableAssociatedFilter,
} from '..';
import type { NotesState } from '..';
Expand Down Expand Up @@ -121,8 +121,8 @@ export const NoteManagementPage = () => {
const pagination = useSelector(selectNotesPagination);
const sort = useSelector(selectNotesTableSort);
const notesSearch = useSelector(selectNotesTableSearch);
const notesUserFilters = useSelector(selectNotesTableUserFilters);
const notesAssociatedFilters = useSelector(selectNotesTableAssociatedFilter);
const notesCreatedByFilter = useSelector(selectNotesTableCreatedByFilter);
const notesAssociatedFilter = useSelector(selectNotesTableAssociatedFilter);
const pendingDeleteIds = useSelector(selectNotesTablePendingDeleteIds);
const isDeleteModalVisible = pendingDeleteIds.length > 0;
const fetchNotesStatus = useSelector(selectFetchNotesStatus);
Expand All @@ -138,8 +138,8 @@ export const NoteManagementPage = () => {
sortField: sort.field,
sortOrder: sort.direction,
filter: '',
userFilter: notesUserFilters,
associatedFilter: notesAssociatedFilters,
createdByFilter: notesCreatedByFilter,
associatedFilter: notesAssociatedFilter,
search: notesSearch,
})
);
Expand All @@ -149,8 +149,8 @@ export const NoteManagementPage = () => {
pagination.perPage,
sort.field,
sort.direction,
notesUserFilters,
notesAssociatedFilters,
notesCreatedByFilter,
notesAssociatedFilter,
notesSearch,
]);

Expand Down
Loading

0 comments on commit 641ff0e

Please sign in to comment.