From 1e77d8d10d35104318285152a39cd14c5fd3c3f6 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com> Date: Mon, 14 Nov 2022 08:27:10 -0500 Subject: [PATCH] [Cases] Assignees enhancements (#144836) WIP This PR implements some enhancements for the assignees feature that wasn't completed in 8.5. Issue: https://github.com/elastic/kibana/issues/141057 Fixes: https://github.com/elastic/kibana/issues/140889 ### List sorting The current user is not brought to the front of lists (only in the popovers). Unknown users are still placed at the end of the list.
Current user is sorted like other users #### Case View Page ![image](https://user-images.githubusercontent.com/56361221/200646181-9744622f-fe11-41c5-97ac-ce7b777d47a1.png) #### Case List Page Avatars ![image](https://user-images.githubusercontent.com/56361221/200646269-b637743f-35f1-48d0-91bd-faee32784613.png)
### Limit assignee selection Leverage the `limit` prop exposed by the `UserProfilesSelectable` here: https://github.com/elastic/kibana/pull/144618
Adding limit message ![image](https://user-images.githubusercontent.com/56361221/200653672-9c195031-3117-4ac9-b6e9-98ac11ee170e.png)
### Show the selected count Show the selected count even when it is zero so the component doesn't jump around.
Selected count #### View case page ![image](https://user-images.githubusercontent.com/56361221/200659972-a6eca466-0d4c-4736-9a2e-62b422f99944.png) #### All cases filter ![image](https://user-images.githubusercontent.com/56361221/200660181-da13092b-6f6a-4b2d-98cd-325ebf8d75b1.png)
### Expandable assignees column Added a button to expand/collapse the assignee avatars column on the all cases list page
Cases list page assignees column ![image](https://user-images.githubusercontent.com/56361221/200891826-08f15531-3a47-40c1-9cc6-12558b645083.png) ![image](https://user-images.githubusercontent.com/56361221/200892014-92cd3142-15d0-4250-b83e-b32b1c9dd03f.png)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/all_cases/all_cases_list.tsx | 1 - .../all_cases/assignees_column.test.tsx | 154 ++++++++++++++++++ .../components/all_cases/assignees_column.tsx | 97 +++++++++++ .../components/all_cases/assignees_filter.tsx | 8 +- .../components/all_cases/translations.ts | 10 ++ .../all_cases/use_cases_columns.test.tsx | 86 +++++++++- .../all_cases/use_cases_columns.tsx | 52 +----- .../case_view/components/assign_users.tsx | 1 - .../components/suggest_users_popover.test.tsx | 4 +- .../components/suggest_users_popover.tsx | 16 +- .../selected_status_message.test.tsx | 24 --- .../user_profiles/selected_status_message.tsx | 22 --- .../components/user_profiles/translations.ts | 7 + .../user_profiles/use_assignees.test.ts | 28 +--- .../containers/user_profiles/use_assignees.ts | 13 +- 15 files changed, 373 insertions(+), 150 deletions(-) create mode 100644 x-pack/plugins/cases/public/components/all_cases/assignees_column.test.tsx create mode 100644 x-pack/plugins/cases/public/components/all_cases/assignees_column.tsx delete mode 100644 x-pack/plugins/cases/public/components/user_profiles/selected_status_message.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/user_profiles/selected_status_message.tsx diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx index f4449c3949003..42ef26d6ba1ac 100644 --- a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx @@ -199,7 +199,6 @@ export const AllCasesList = React.memo( const { columns } = useCasesColumns({ filterStatus: filterOptions.status ?? StatusAll, userProfiles: userProfiles ?? new Map(), - currentUserProfile, isSelectorView, connectors, onRowClick, diff --git a/x-pack/plugins/cases/public/components/all_cases/assignees_column.test.tsx b/x-pack/plugins/cases/public/components/all_cases/assignees_column.test.tsx new file mode 100644 index 0000000000000..c0f33130a1137 --- /dev/null +++ b/x-pack/plugins/cases/public/components/all_cases/assignees_column.test.tsx @@ -0,0 +1,154 @@ +/* + * 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 React from 'react'; +import { screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import type { AppMockRenderer } from '../../common/mock'; +import { createAppMockRenderer } from '../../common/mock'; +import { userProfiles, userProfilesMap } from '../../containers/user_profiles/api.mock'; +import type { AssigneesColumnProps } from './assignees_column'; +import { AssigneesColumn } from './assignees_column'; + +describe('AssigneesColumn', () => { + const defaultProps: AssigneesColumnProps = { + assignees: userProfiles, + userProfiles: userProfilesMap, + compressedDisplayLimit: 2, + }; + + let appMockRender: AppMockRenderer; + + beforeEach(() => { + jest.clearAllMocks(); + + appMockRender = createAppMockRenderer(); + }); + + it('renders a long dash if the assignees is an empty array', async () => { + const props = { + ...defaultProps, + assignees: [], + }; + + appMockRender.render(); + + expect( + screen.queryByTestId('case-table-column-assignee-damaged_raccoon') + ).not.toBeInTheDocument(); + expect(screen.queryByTestId('case-table-column-expand-button')).not.toBeInTheDocument(); + // u2014 is the unicode for a long dash + expect(screen.getByText('\u2014')).toBeInTheDocument(); + }); + + it('only renders 2 avatars when the limit is 2', async () => { + const props = { + ...defaultProps, + }; + + appMockRender.render(); + + expect(screen.getByTestId('case-table-column-assignee-damaged_raccoon')).toBeInTheDocument(); + expect(screen.getByTestId('case-table-column-assignee-physical_dinosaur')).toBeInTheDocument(); + }); + + it('renders all 3 avatars when the limit is 5', async () => { + const props = { + ...defaultProps, + compressedDisplayLimit: 5, + }; + + appMockRender.render(); + + expect(screen.getByTestId('case-table-column-assignee-damaged_raccoon')).toBeInTheDocument(); + expect(screen.getByTestId('case-table-column-assignee-physical_dinosaur')).toBeInTheDocument(); + expect(screen.getByTestId('case-table-column-assignee-wet_dingo')).toBeInTheDocument(); + }); + + it('shows the show more avatars button when the limit is 2', async () => { + const props = { + ...defaultProps, + compressedDisplayLimit: 2, + }; + + appMockRender.render(); + + expect(screen.getByTestId('case-table-column-expand-button')).toBeInTheDocument(); + expect(screen.getByText('+1 more')).toBeInTheDocument(); + }); + + it('does not show the show more button when the limit is 5', async () => { + const props = { + ...defaultProps, + compressedDisplayLimit: 5, + }; + + appMockRender.render(); + + expect(screen.queryByTestId('case-table-column-expand-button')).not.toBeInTheDocument(); + }); + + it('does not show the show more button when the limit is the same number of the assignees', async () => { + const props = { + ...defaultProps, + compressedDisplayLimit: userProfiles.length, + }; + + appMockRender.render(); + + expect(screen.queryByTestId('case-table-column-expand-button')).not.toBeInTheDocument(); + }); + + it('displays the show less avatars button when the show more is clicked', async () => { + const props = { + ...defaultProps, + compressedDisplayLimit: 2, + }; + + appMockRender.render(); + + expect(screen.queryByTestId('case-table-column-assignee-wet_dingo')).not.toBeInTheDocument(); + + expect(screen.getByTestId('case-table-column-expand-button')).toBeInTheDocument(); + expect(screen.getByText('+1 more')).toBeInTheDocument(); + + userEvent.click(screen.getByTestId('case-table-column-expand-button')); + + await waitFor(() => { + expect(screen.getByText('show less')).toBeInTheDocument(); + expect(screen.getByTestId('case-table-column-assignee-wet_dingo')).toBeInTheDocument(); + }); + }); + + it('shows more avatars and then hides them when the expand row button is clicked multiple times', async () => { + const props = { + ...defaultProps, + compressedDisplayLimit: 2, + }; + + appMockRender.render(); + + expect(screen.queryByTestId('case-table-column-assignee-wet_dingo')).not.toBeInTheDocument(); + + expect(screen.getByTestId('case-table-column-expand-button')).toBeInTheDocument(); + expect(screen.getByText('+1 more')).toBeInTheDocument(); + + userEvent.click(screen.getByTestId('case-table-column-expand-button')); + + await waitFor(() => { + expect(screen.getByText('show less')).toBeInTheDocument(); + expect(screen.getByTestId('case-table-column-assignee-wet_dingo')).toBeInTheDocument(); + }); + + userEvent.click(screen.getByTestId('case-table-column-expand-button')); + + await waitFor(() => { + expect(screen.getByText('+1 more')).toBeInTheDocument(); + expect(screen.queryByTestId('case-table-column-assignee-wet_dingo')).not.toBeInTheDocument(); + }); + }); +}); diff --git a/x-pack/plugins/cases/public/components/all_cases/assignees_column.tsx b/x-pack/plugins/cases/public/components/all_cases/assignees_column.tsx new file mode 100644 index 0000000000000..8b5444f9a9d91 --- /dev/null +++ b/x-pack/plugins/cases/public/components/all_cases/assignees_column.tsx @@ -0,0 +1,97 @@ +/* + * 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 React, { useCallback, useMemo, useState } from 'react'; +import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import type { UserProfileWithAvatar } from '@kbn/user-profile-components'; +import type { Case } from '../../../common/ui/types'; +import { getEmptyTagValue } from '../empty_value'; +import { UserToolTip } from '../user_profiles/user_tooltip'; +import { useAssignees } from '../../containers/user_profiles/use_assignees'; +import { getUsernameDataTestSubj } from '../user_profiles/data_test_subject'; +import { SmallUserAvatar } from '../user_profiles/small_user_avatar'; +import * as i18n from './translations'; + +const COMPRESSED_AVATAR_LIMIT = 3; + +export interface AssigneesColumnProps { + assignees: Case['assignees']; + userProfiles: Map; + compressedDisplayLimit?: number; +} + +const AssigneesColumnComponent: React.FC = ({ + assignees, + userProfiles, + compressedDisplayLimit = COMPRESSED_AVATAR_LIMIT, +}) => { + const [isAvatarListExpanded, setIsAvatarListExpanded] = useState(false); + + const { allAssignees } = useAssignees({ + caseAssignees: assignees, + userProfiles, + }); + + const toggleExpandedAvatars = useCallback( + () => setIsAvatarListExpanded((prevState) => !prevState), + [] + ); + + const numHiddenAvatars = allAssignees.length - compressedDisplayLimit; + const shouldShowExpandListButton = numHiddenAvatars > 0; + + const limitedAvatars = useMemo( + () => allAssignees.slice(0, compressedDisplayLimit), + [allAssignees, compressedDisplayLimit] + ); + + const avatarsToDisplay = useMemo(() => { + if (isAvatarListExpanded || !shouldShowExpandListButton) { + return allAssignees; + } + + return limitedAvatars; + }, [allAssignees, isAvatarListExpanded, limitedAvatars, shouldShowExpandListButton]); + + if (allAssignees.length <= 0) { + return getEmptyTagValue(); + } + + return ( + + {avatarsToDisplay.map((assignee) => { + const dataTestSubjName = getUsernameDataTestSubj(assignee); + return ( + + + + + + ); + })} + + {shouldShowExpandListButton ? ( + + {isAvatarListExpanded ? i18n.SHOW_LESS : i18n.SHOW_MORE(numHiddenAvatars)} + + ) : null} + + ); +}; + +AssigneesColumnComponent.displayName = 'AssigneesColumn'; + +export const AssigneesColumn = React.memo(AssigneesColumnComponent); diff --git a/x-pack/plugins/cases/public/components/all_cases/assignees_filter.tsx b/x-pack/plugins/cases/public/components/all_cases/assignees_filter.tsx index 67086f44e1fef..1f9118010ba16 100644 --- a/x-pack/plugins/cases/public/components/all_cases/assignees_filter.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/assignees_filter.tsx @@ -15,7 +15,6 @@ import { useCasesContext } from '../cases_context/use_cases_context'; import type { CurrentUserProfile } from '../types'; import { EmptyMessage } from '../user_profiles/empty_message'; import { NoMatches } from '../user_profiles/no_matches'; -import { SelectedStatusMessage } from '../user_profiles/selected_status_message'; import { bringCurrentUserToFrontAndSort, orderAssigneesIncludingNone } from '../user_profiles/sort'; import type { AssigneesFilteringSelection } from '../user_profiles/types'; import * as i18n from './translations'; @@ -53,12 +52,7 @@ const AssigneesFilterPopoverComponent: React.FC = ( ); const selectedStatusMessage = useCallback( - (selectedCount: number) => ( - - ), + (selectedCount: number) => i18n.TOTAL_ASSIGNEES_FILTERED(selectedCount), [] ); diff --git a/x-pack/plugins/cases/public/components/all_cases/translations.ts b/x-pack/plugins/cases/public/components/all_cases/translations.ts index aedfefd35c360..1a215cd1ef889 100644 --- a/x-pack/plugins/cases/public/components/all_cases/translations.ts +++ b/x-pack/plugins/cases/public/components/all_cases/translations.ts @@ -137,3 +137,13 @@ export const NO_ASSIGNEES = i18n.translate( defaultMessage: 'No assignees', } ); + +export const SHOW_LESS = i18n.translate('xpack.cases.allCasesView.showLessAvatars', { + defaultMessage: 'show less', +}); + +export const SHOW_MORE = (count: number) => + i18n.translate('xpack.cases.allCasesView.showMoreAvatars', { + defaultMessage: '+{count} more', + values: { count }, + }); diff --git a/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.test.tsx b/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.test.tsx index 5ae7556e6bd29..0647bdd25e382 100644 --- a/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.test.tsx @@ -18,14 +18,13 @@ import type { AppMockRenderer } from '../../common/mock'; import { createAppMockRenderer, readCasesPermissions, TestProviders } from '../../common/mock'; import { renderHook } from '@testing-library/react-hooks'; import { CaseStatuses } from '../../../common'; -import { userProfilesMap, userProfiles } from '../../containers/user_profiles/api.mock'; +import { userProfilesMap } from '../../containers/user_profiles/api.mock'; describe('useCasesColumns ', () => { let appMockRender: AppMockRenderer; const useCasesColumnsProps: GetCasesColumn = { filterStatus: CaseStatuses.open, userProfiles: userProfilesMap, - currentUserProfile: userProfiles[0], isSelectorView: false, showSolutionColumn: true, }; @@ -58,6 +57,7 @@ describe('useCasesColumns ', () => { "field": "assignees", "name": "Assignees", "render": [Function], + "width": "180px", }, Object { "field": "tags", @@ -112,6 +112,86 @@ describe('useCasesColumns ', () => { `); }); + it('returns the assignees column without the width specified when in the modal view', async () => { + const license = licensingMock.createLicense({ + license: { type: 'platinum' }, + }); + + appMockRender = createAppMockRenderer({ license }); + + const { result } = renderHook( + () => useCasesColumns({ ...useCasesColumnsProps, isSelectorView: true }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + expect(result.current).toMatchInlineSnapshot(` + Object { + "columns": Array [ + Object { + "name": "Name", + "render": [Function], + "width": "20%", + }, + Object { + "field": "assignees", + "name": "Assignees", + "render": [Function], + "width": undefined, + }, + Object { + "field": "tags", + "name": "Tags", + "render": [Function], + "width": "15%", + }, + Object { + "align": "right", + "field": "totalAlerts", + "name": "Alerts", + "render": [Function], + "width": "80px", + }, + Object { + "align": "right", + "field": "owner", + "name": "Solution", + "render": [Function], + }, + Object { + "align": "right", + "field": "totalComment", + "name": "Comments", + "render": [Function], + }, + Object { + "field": "createdAt", + "name": "Created on", + "render": [Function], + "sortable": true, + }, + Object { + "name": "External Incident", + "render": [Function], + }, + Object { + "name": "Status", + "render": [Function], + }, + Object { + "name": "Severity", + "render": [Function], + }, + Object { + "align": "right", + "render": [Function], + }, + ], + } + `); + }); + it('does not render the solution columns', async () => { const license = licensingMock.createLicense({ license: { type: 'platinum' }, @@ -138,6 +218,7 @@ describe('useCasesColumns ', () => { "field": "assignees", "name": "Assignees", "render": [Function], + "width": "180px", }, Object { "field": "tags", @@ -209,6 +290,7 @@ describe('useCasesColumns ', () => { "field": "assignees", "name": "Assignees", "render": [Function], + "width": "180px", }, Object { "field": "tags", diff --git a/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.tsx b/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.tsx index a1c845ad94f47..cb08bf4b6e526 100644 --- a/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/use_cases_columns.tsx @@ -43,12 +43,8 @@ import { TruncatedText } from '../truncated_text'; import { getConnectorIcon } from '../utils'; import type { CasesOwners } from '../../client/helpers/can_use_cases'; import { severities } from '../severity/config'; -import { UserToolTip } from '../user_profiles/user_tooltip'; -import { useAssignees } from '../../containers/user_profiles/use_assignees'; -import { getUsernameDataTestSubj } from '../user_profiles/data_test_subject'; -import type { CurrentUserProfile } from '../types'; -import { SmallUserAvatar } from '../user_profiles/small_user_avatar'; import { useCasesFeatures } from '../../common/use_cases_features'; +import { AssigneesColumn } from './assignees_column'; type CasesColumns = | EuiTableActionsColumnType @@ -76,47 +72,9 @@ const StyledEuiBadge = euiStyled(EuiBadge)` const renderStringField = (field: string, dataTestSubj: string) => field != null ? {field} : getEmptyTagValue(); -const AssigneesColumn: React.FC<{ - assignees: Case['assignees']; - userProfiles: Map; - currentUserProfile: CurrentUserProfile; -}> = ({ assignees, userProfiles, currentUserProfile }) => { - const { allAssignees } = useAssignees({ - caseAssignees: assignees, - userProfiles, - currentUserProfile, - }); - - if (allAssignees.length <= 0) { - return getEmptyTagValue(); - } - - return ( - - {allAssignees.map((assignee) => { - const dataTestSubjName = getUsernameDataTestSubj(assignee); - return ( - - - - - - ); - })} - - ); -}; - -AssigneesColumn.displayName = 'AssigneesColumn'; - export interface GetCasesColumn { filterStatus: string; userProfiles: Map; - currentUserProfile: CurrentUserProfile; isSelectorView: boolean; connectors?: ActionConnector[]; onRowClick?: (theCase: Case) => void; @@ -131,7 +89,6 @@ export interface UseCasesColumnsReturnValue { export const useCasesColumns = ({ filterStatus, userProfiles, - currentUserProfile, isSelectorView, connectors = [], onRowClick, @@ -184,12 +141,9 @@ export const useCasesColumns = ({ field: 'assignees', name: i18n.ASSIGNEES, render: (assignees: Case['assignees']) => ( - + ), + width: !isSelectorView ? '180px' : undefined, }); } diff --git a/x-pack/plugins/cases/public/components/case_view/components/assign_users.tsx b/x-pack/plugins/cases/public/components/case_view/components/assign_users.tsx index 90d227901259a..bd09c49597d9e 100644 --- a/x-pack/plugins/cases/public/components/case_view/components/assign_users.tsx +++ b/x-pack/plugins/cases/public/components/case_view/components/assign_users.tsx @@ -104,7 +104,6 @@ const AssignUsersComponent: React.FC = ({ const { assigneesWithProfiles, assigneesWithoutProfiles, allAssignees } = useAssignees({ caseAssignees, userProfiles, - currentUserProfile, }); const [selectedAssignees, setSelectedAssignees] = useState(); diff --git a/x-pack/plugins/cases/public/components/case_view/components/suggest_users_popover.test.tsx b/x-pack/plugins/cases/public/components/case_view/components/suggest_users_popover.test.tsx index bf22c764290aa..479b8e39d232d 100644 --- a/x-pack/plugins/cases/public/components/case_view/components/suggest_users_popover.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/components/suggest_users_popover.test.tsx @@ -36,7 +36,7 @@ describe('SuggestUsersPopover', () => { }; }); - it.skip('calls onUsersChange when 1 user is selected', async () => { + it('calls onUsersChange when 1 user is selected', async () => { const onUsersChange = jest.fn(); const props = { ...defaultProps, onUsersChange }; appMockRender.render(); @@ -182,7 +182,7 @@ describe('SuggestUsersPopover', () => { expect(screen.getByText('1 assigned')).toBeInTheDocument(); }); - it.skip('shows the 1 assigned total after clicking on a user', async () => { + it('shows the 1 assigned total after clicking on a user', async () => { appMockRender.render(); await waitForEuiPopoverOpen(); diff --git a/x-pack/plugins/cases/public/components/case_view/components/suggest_users_popover.tsx b/x-pack/plugins/cases/public/components/case_view/components/suggest_users_popover.tsx index fd73d3a2ae7ce..af3883257fdd4 100644 --- a/x-pack/plugins/cases/public/components/case_view/components/suggest_users_popover.tsx +++ b/x-pack/plugins/cases/public/components/case_view/components/suggest_users_popover.tsx @@ -11,12 +11,12 @@ import { UserProfilesPopover } from '@kbn/user-profile-components'; import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; import { isEmpty } from 'lodash'; +import { MAX_ASSIGNEES_PER_CASE } from '../../../../common/constants'; import { useSuggestUserProfiles } from '../../../containers/user_profiles/use_suggest_user_profiles'; import { useCasesContext } from '../../cases_context/use_cases_context'; import type { AssigneeWithProfile } from '../../user_profiles/types'; import * as i18n from '../translations'; import { bringCurrentUserToFrontAndSort } from '../../user_profiles/sort'; -import { SelectedStatusMessage } from '../../user_profiles/selected_status_message'; import { EmptyMessage } from '../../user_profiles/empty_message'; import { NoMatches } from '../../user_profiles/no_matches'; import type { CurrentUserProfile } from '../../types'; @@ -79,12 +79,12 @@ const SuggestUsersPopoverComponent: React.FC = ({ ); const selectedStatusMessage = useCallback( - (selectedCount: number) => ( - - ), + (selectedCount: number) => i18n.TOTAL_USERS_ASSIGNED(selectedCount), + [] + ); + + const limitReachedMessage = useCallback( + (limit: number) => i18n.MAX_SELECTED_ASSIGNEES(limit), [] ); @@ -131,6 +131,8 @@ const SuggestUsersPopoverComponent: React.FC = ({ selectedOptions: selectedUsers ?? selectedProfiles, isLoading: isLoadingData, height: 'full', + limit: MAX_ASSIGNEES_PER_CASE, + limitReachedMessage, searchPlaceholder: i18n.SEARCH_USERS, clearButtonLabel: i18n.REMOVE_ASSIGNEES, emptyMessage: , diff --git a/x-pack/plugins/cases/public/components/user_profiles/selected_status_message.test.tsx b/x-pack/plugins/cases/public/components/user_profiles/selected_status_message.test.tsx deleted file mode 100644 index b9611bb683d44..0000000000000 --- a/x-pack/plugins/cases/public/components/user_profiles/selected_status_message.test.tsx +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 React from 'react'; -import { render, screen } from '@testing-library/react'; -import { SelectedStatusMessage } from './selected_status_message'; - -describe('SelectedStatusMessage', () => { - it('does not render if the count is 0', () => { - const { container } = render(); - - expect(container.firstChild).toBeNull(); - expect(screen.queryByText('hello')).not.toBeInTheDocument(); - }); - - it('renders the message when the count is great than 0', () => { - render(); - - expect(screen.getByText('hello')).toBeInTheDocument(); - }); -}); diff --git a/x-pack/plugins/cases/public/components/user_profiles/selected_status_message.tsx b/x-pack/plugins/cases/public/components/user_profiles/selected_status_message.tsx deleted file mode 100644 index 87839fb7c3482..0000000000000 --- a/x-pack/plugins/cases/public/components/user_profiles/selected_status_message.tsx +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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 React from 'react'; - -const SelectedStatusMessageComponent: React.FC<{ - selectedCount: number; - message: string; -}> = ({ selectedCount, message }) => { - if (selectedCount <= 0) { - return null; - } - - return <>{message}; -}; -SelectedStatusMessageComponent.displayName = 'SelectedStatusMessage'; - -export const SelectedStatusMessage = React.memo(SelectedStatusMessageComponent); diff --git a/x-pack/plugins/cases/public/components/user_profiles/translations.ts b/x-pack/plugins/cases/public/components/user_profiles/translations.ts index 2624ec834cd2e..f3c338c4d7b4e 100644 --- a/x-pack/plugins/cases/public/components/user_profiles/translations.ts +++ b/x-pack/plugins/cases/public/components/user_profiles/translations.ts @@ -65,3 +65,10 @@ export const INVALID_ASSIGNEES = i18n.translate('xpack.cases.create.invalidAssig maxAssignees: MAX_ASSIGNEES_PER_CASE, }, }); + +export const MAX_SELECTED_ASSIGNEES = (limit: number) => + i18n.translate('xpack.cases.userProfile.maxSelectedAssignees', { + defaultMessage: + "You've selected the maximum number of {count, plural, one {# assignee} other {# assignees}}", + values: { count: limit }, + }); diff --git a/x-pack/plugins/cases/public/containers/user_profiles/use_assignees.test.ts b/x-pack/plugins/cases/public/containers/user_profiles/use_assignees.test.ts index f8b38e4d31dbf..3622b66aef006 100644 --- a/x-pack/plugins/cases/public/containers/user_profiles/use_assignees.test.ts +++ b/x-pack/plugins/cases/public/containers/user_profiles/use_assignees.test.ts @@ -13,7 +13,7 @@ import { useAssignees } from './use_assignees'; describe('useAssignees', () => { it('returns an empty array when the caseAssignees is empty', () => { const { result } = renderHook(() => - useAssignees({ caseAssignees: [], userProfiles: new Map(), currentUserProfile: undefined }) + useAssignees({ caseAssignees: [], userProfiles: new Map() }) ); expect(result.current.allAssignees).toHaveLength(0); @@ -26,7 +26,6 @@ describe('useAssignees', () => { useAssignees({ caseAssignees: userProfiles.map((profile) => ({ uid: profile.uid })), userProfiles: userProfilesMap, - currentUserProfile: undefined, }) ); @@ -41,7 +40,6 @@ describe('useAssignees', () => { useAssignees({ caseAssignees: unsorted.map((profile) => ({ uid: profile.uid })), userProfiles: userProfilesMap, - currentUserProfile: undefined, }) ); @@ -56,7 +54,6 @@ describe('useAssignees', () => { useAssignees({ caseAssignees: unknownProfiles, userProfiles: userProfilesMap, - currentUserProfile: undefined, }) ); @@ -71,7 +68,6 @@ describe('useAssignees', () => { useAssignees({ caseAssignees: assignees, userProfiles: userProfilesMap, - currentUserProfile: undefined, }) ); @@ -86,28 +82,6 @@ describe('useAssignees', () => { { uid: '1' }, ]); }); - - it('returns assignees with profiles with the current user at the front', () => { - const { result } = renderHook(() => - useAssignees({ - caseAssignees: userProfiles, - userProfiles: userProfilesMap, - currentUserProfile: userProfiles[2], - }) - ); - - expect(result.current.assigneesWithProfiles).toHaveLength(3); - expect(result.current.allAssignees).toHaveLength(3); - - const asAssignees = userProfiles.map(asAssigneeWithProfile); - - expect(result.current.assigneesWithProfiles).toEqual([ - asAssignees[2], - asAssignees[0], - asAssignees[1], - ]); - expect(result.current.allAssignees).toEqual([asAssignees[2], asAssignees[0], asAssignees[1]]); - }); }); const asAssigneeWithProfile = (profile: UserProfileWithAvatar) => ({ uid: profile.uid, profile }); diff --git a/x-pack/plugins/cases/public/containers/user_profiles/use_assignees.ts b/x-pack/plugins/cases/public/containers/user_profiles/use_assignees.ts index 069eae715f2b9..8e1f15d7d979a 100644 --- a/x-pack/plugins/cases/public/containers/user_profiles/use_assignees.ts +++ b/x-pack/plugins/cases/public/containers/user_profiles/use_assignees.ts @@ -8,8 +8,7 @@ import type { UserProfileWithAvatar } from '@kbn/user-profile-components'; import { useMemo } from 'react'; import type { CaseAssignees } from '../../../common/api'; -import type { CurrentUserProfile } from '../../components/types'; -import { bringCurrentUserToFrontAndSort } from '../../components/user_profiles/sort'; +import { sortProfiles } from '../../components/user_profiles/sort'; import type { Assignee, AssigneeWithProfile } from '../../components/user_profiles/types'; interface PartitionedAssignees { @@ -20,11 +19,9 @@ interface PartitionedAssignees { export const useAssignees = ({ caseAssignees, userProfiles, - currentUserProfile, }: { caseAssignees: CaseAssignees; userProfiles: Map; - currentUserProfile: CurrentUserProfile; }): { assigneesWithProfiles: AssigneeWithProfile[]; assigneesWithoutProfiles: Assignee[]; @@ -46,14 +43,14 @@ export const useAssignees = ({ { usersWithProfiles: [], usersWithoutProfiles: [] } ); - const orderedProf = bringCurrentUserToFrontAndSort(currentUserProfile, usersWithProfiles); + const orderedProf = sortProfiles(usersWithProfiles); - const assigneesWithProfile2 = orderedProf?.map((profile) => ({ uid: profile.uid, profile })); + const withProfiles = orderedProf?.map((profile) => ({ uid: profile.uid, profile })); return { - assigneesWithProfiles: assigneesWithProfile2 ?? [], + assigneesWithProfiles: withProfiles ?? [], assigneesWithoutProfiles: usersWithoutProfiles, }; - }, [caseAssignees, currentUserProfile, userProfiles]); + }, [caseAssignees, userProfiles]); const allAssignees = useMemo( () => [...assigneesWithProfiles, ...assigneesWithoutProfiles],