From 805830085edb4f758452f2ccb94def3672e6b9ee Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 20 Jan 2025 09:03:38 +0100 Subject: [PATCH] [ResponseOps][Cases] Save sortOrder in local storage (#206443) Fixes /~https://github.com/elastic/security-team/issues/11357 ## Summary In this PR we use cases local storage to preserve the selection of ordering in the user activity on the cases detail page. Initially, I was going to save the whole `UserActivityParams` on local storage but ultimately decided against it just to preserve the defaults like "selected tab" or "page". --- .../components/case_view_activity.test.tsx | 27 +++++++++++++++++++ .../components/case_view_activity.tsx | 18 ++++++++++--- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_activity.test.tsx b/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_activity.test.tsx index 5a94b440a2d8b..1981ddc671c65 100644 --- a/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_activity.test.tsx +++ b/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_activity.test.tsx @@ -147,6 +147,8 @@ const useOnUpdateFieldMock = useOnUpdateField as jest.Mock; const useCasesFeaturesMock = useCasesFeatures as jest.Mock; const useReplaceCustomFieldMock = useReplaceCustomField as jest.Mock; +const localStorageKey = `${basicCase.owner}.cases.userActivity.sortOrder`; + describe('Case View Page activity tab', () => { let appMockRender: AppMockRenderer; const caseConnectors = getCaseConnectorsMockResponse(); @@ -217,6 +219,8 @@ describe('Case View Page activity tab', () => { jest.clearAllMocks(); appMockRender = createAppMockRenderer(); + localStorage.clear(); + useGetCaseUsersMock.mockReturnValue({ isLoading: false, data: caseUsers }); useCasesFeaturesMock.mockReturnValue(useGetCasesFeaturesRes); }); @@ -391,6 +395,29 @@ describe('Case View Page activity tab', () => { expect(await screen.findByTestId('case-view-edit-connector')).toBeInTheDocument(); }); + it('should save sortOrder in localstorage', async () => { + (useGetCaseConfiguration as jest.Mock).mockReturnValue({ + data: { + customFields: [customFieldsConfigurationMock[1]], + observableTypes: [], + }, + }); + + appMockRender.render( + + ); + + await userEvent.selectOptions(await screen.findByTestId('user-actions-sort-select'), 'desc'); + + expect(localStorage.getItem(localStorageKey)).toBe('"desc"'); + }); + describe('filter activity', () => { beforeEach(() => { jest.clearAllMocks(); diff --git a/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_activity.tsx b/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_activity.tsx index 6e945ef836272..e92edc0899bc3 100644 --- a/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_activity.tsx +++ b/x-pack/platform/plugins/shared/cases/public/components/case_view/components/case_view_activity.tsx @@ -16,6 +16,7 @@ import { } from '@elastic/eui'; import React, { useCallback, useMemo, useState } from 'react'; import { isEqual } from 'lodash'; +import { useCasesLocalStorage } from '../../../common/use_cases_local_storage'; import { useGetCaseConfiguration } from '../../../containers/configure/use_get_case_configuration'; import { useGetCaseUsers } from '../../../containers/use_get_case_users'; import { useGetCaseConnectors } from '../../../containers/use_get_case_connectors'; @@ -40,7 +41,10 @@ import { useGetCaseUserActionsStats } from '../../../containers/use_get_case_use import { AssignUsers } from './assign_users'; import { UserActionsActivityBar } from '../../user_actions_activity_bar'; import type { Assignee } from '../../user_profiles/types'; -import type { UserActivityParams } from '../../user_actions_activity_bar/types'; +import type { + UserActivityParams, + UserActivitySortOrder, +} from '../../user_actions_activity_bar/types'; import { CASE_VIEW_PAGE_TABS } from '../../../../common/types'; import { CaseViewTabs } from '../case_view_tabs'; import { Description } from '../../description'; @@ -49,6 +53,8 @@ import { parseCaseUsers } from '../../utils'; import { CustomFields } from './custom_fields'; import { useReplaceCustomField } from '../../../containers/use_replace_custom_field'; +const LOCALSTORAGE_SORT_ORDER_KEY = 'cases.userActivity.sortOrder'; + export const CaseViewActivity = ({ ruleDetailsNavigation, caseData, @@ -62,9 +68,14 @@ export const CaseViewActivity = ({ showAlertDetails?: (alertId: string, index: string) => void; useFetchAlertData: UseFetchAlertData; }) => { + const [sortOrder, setSortOrder] = useCasesLocalStorage( + LOCALSTORAGE_SORT_ORDER_KEY, + 'asc' + ); + const [userActivityQueryParams, setUserActivityQueryParams] = useState({ type: 'all', - sortOrder: 'asc', + sortOrder, page: 1, perPage: 10, }); @@ -167,6 +178,7 @@ export const CaseViewActivity = ({ const handleUserActionsActivityChanged = useCallback( (params: UserActivityParams) => { + setSortOrder(params.sortOrder); setUserActivityQueryParams((oldParams) => ({ ...oldParams, page: 1, @@ -174,7 +186,7 @@ export const CaseViewActivity = ({ sortOrder: params.sortOrder, })); }, - [setUserActivityQueryParams] + [setSortOrder, setUserActivityQueryParams] ); const showUserActions =