From b3617df05ae51bb510de47e7b3b334be5a7c548f Mon Sep 17 00:00:00 2001 From: Janki Salvi <117571355+js-jankisalvi@users.noreply.github.com> Date: Thu, 17 Aug 2023 18:07:25 +0200 Subject: [PATCH 01/10] add warning label when max cases limit reached --- .../public/components/all_cases/table.tsx | 1 + .../components/all_cases/translations.ts | 20 +++++- .../components/all_cases/utility_bar.test.tsx | 5 ++ .../components/all_cases/utility_bar.tsx | 63 +++++++++++++++++-- 4 files changed, 82 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/cases/public/components/all_cases/table.tsx b/x-pack/plugins/cases/public/components/all_cases/table.tsx index dc5e7042ec9f7..ae60e942dc75a 100644 --- a/x-pack/plugins/cases/public/components/all_cases/table.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/table.tsx @@ -79,6 +79,7 @@ export const CasesTable: FunctionComponent = ({ ) : ( <> defaultMessage: 'Selected {totalRules} {totalRules, plural, =1 {case} other {cases}}', }); -export const SHOWING_CASES = (totalRules: number) => +export const SHOWING_CASES = (totalCases: number, pageSize: number) => i18n.translate('xpack.cases.caseTable.showingCasesTitle', { - values: { totalRules }, - defaultMessage: 'Showing {totalRules} {totalRules, plural, =1 {case} other {cases}}', + values: { totalCases, pageSize }, + defaultMessage: 'Showing {pageSize} of {totalCases} {totalCases, plural, =1 {case} other {cases}}', + }); + +export const MAX_CASES = (totalCases: number) => + i18n.translate('xpack.cases.caseTable.maxCases', { + values: { totalCases }, + defaultMessage: 'The results were capped at {totalCases} to maintain performance. Try limiting your search to reduce the results.', }); +export const DISMISS = i18n.translate('xpack.cases.caseTable.dismiss', { + defaultMessage: 'Dismiss', +}); + +export const NOT_SHOW_AGAIN = i18n.translate('xpack.cases.caseTable.notShowAgain', { + defaultMessage: 'Do not show again', +}); + export const UNIT = (totalCount: number) => i18n.translate('xpack.cases.caseTable.unit', { values: { totalCount }, diff --git a/x-pack/plugins/cases/public/components/all_cases/utility_bar.test.tsx b/x-pack/plugins/cases/public/components/all_cases/utility_bar.test.tsx index 2fddd22a9083c..d0f52a8457c95 100644 --- a/x-pack/plugins/cases/public/components/all_cases/utility_bar.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/utility_bar.test.tsx @@ -27,6 +27,11 @@ describe('Severity form field', () => { totalCases: 5, selectedCases: [basicCase], deselectCases, + pagination: { + pageIndex: 1, + pageSize: 10, + totalItemCount: 5, + } }; beforeEach(() => { diff --git a/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx b/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx index 4afc8e7e681a0..93b04f021edac 100644 --- a/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx @@ -6,6 +6,8 @@ */ import type { FunctionComponent } from 'react'; +import { css } from '@emotion/react'; +import useLocalStorage from 'react-use/lib/useLocalStorage'; import React, { useCallback, useState } from 'react'; import { EuiButtonEmpty, @@ -15,9 +17,13 @@ import { EuiPopover, EuiText, useEuiTheme, + Pagination, + EuiCallOut, + EuiSpacer, } from '@elastic/eui'; import * as i18n from './translations'; import type { CasesUI } from '../../../common/ui/types'; +import { MAX_DOCS_PER_PAGE } from '../../../common/constants'; import { useRefreshCases } from './use_on_refresh_cases'; import { useBulkActions } from './use_bulk_actions'; import { useCasesContext } from '../cases_context/use_cases_context'; @@ -27,16 +33,24 @@ interface Props { totalCases: number; selectedCases: CasesUI; deselectCases: () => void; + pagination: Pagination; } export const CasesTableUtilityBar: FunctionComponent = React.memo( - ({ isSelectorView, totalCases, selectedCases, deselectCases }) => { + ({ isSelectorView, totalCases, selectedCases, deselectCases, pagination }) => { const { euiTheme } = useEuiTheme(); + const refreshCases = useRefreshCases(); + const { permissions, appId } = useCasesContext(); + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const [isMessageDismissed, setIsMessageDismissed] = useState(false); + const localStorageKey = `cases.${appId}.utilityBar.hideMaxLimitWarning`; + const [localStorageWarning, setLocalStorageWarning] = useLocalStorage(localStorageKey); + const togglePopover = useCallback(() => setIsPopoverOpen(!isPopoverOpen), [isPopoverOpen]); const closePopover = useCallback(() => setIsPopoverOpen(false), []); - const refreshCases = useRefreshCases(); - const { permissions } = useCasesContext(); + + const toggleWarning = useCallback(() => setIsMessageDismissed(!isMessageDismissed), [isMessageDismissed]); const onRefresh = useCallback(() => { deselectCases(); @@ -49,6 +63,10 @@ export const CasesTableUtilityBar: FunctionComponent = React.memo( onActionSuccess: onRefresh, }); + const handleNotShowAgain = () => { + setLocalStorageWarning(true); + }; + /** * At least update or delete permissions needed to show bulk actions. * Granular permission check for each action is performed @@ -56,6 +74,26 @@ export const CasesTableUtilityBar: FunctionComponent = React.memo( */ const showBulkActions = (permissions.update || permissions.delete) && selectedCases.length > 0; + const visibleCases = totalCases > pagination.pageSize ? pagination.pageSize : totalCases; + + const hasReachedMaxCases = totalCases > MAX_DOCS_PER_PAGE && (pagination.pageSize * (pagination.pageIndex + 1)) >= MAX_DOCS_PER_PAGE; + + const isDoNotShowAgainSelected = localStorageWarning && localStorageWarning === true; + + const renderMaxLengthWarning = (): React.ReactNode => ( + + + {i18n.MAX_CASES(totalCases)} + + + {i18n.DISMISS} + + + {i18n.NOT_SHOW_AGAIN} + + + ); + return ( <> = React.memo( }} > - {i18n.SHOWING_CASES(totalCases)} + {i18n.SHOWING_CASES(totalCases, visibleCases)} @@ -136,6 +174,23 @@ export const CasesTableUtilityBar: FunctionComponent = React.memo( {modals} {flyouts} + {hasReachedMaxCases && !isMessageDismissed && !isDoNotShowAgainSelected + ? ( + <> + + + + + + + + + ) : null} ); } From ccd0378c714e6eed7436e2d174177c4e2d0231c2 Mon Sep 17 00:00:00 2001 From: Janki Salvi <117571355+js-jankisalvi@users.noreply.github.com> Date: Mon, 21 Aug 2023 13:18:27 +0200 Subject: [PATCH 02/10] add unit tests --- .../components/all_cases/utility_bar.test.tsx | 108 +++++++++++++++++- .../components/all_cases/utility_bar.tsx | 8 +- 2 files changed, 106 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/cases/public/components/all_cases/utility_bar.test.tsx b/x-pack/plugins/cases/public/components/all_cases/utility_bar.test.tsx index d0f52a8457c95..ab18bf1870806 100644 --- a/x-pack/plugins/cases/public/components/all_cases/utility_bar.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/utility_bar.test.tsx @@ -5,10 +5,11 @@ * 2.0. */ -import { act, waitFor } from '@testing-library/react'; +import { act, waitFor, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import type { AppMockRenderer } from '../../common/mock'; +import { MAX_DOCS_PER_PAGE } from '../../../common/constants'; import { noCasesPermissions, onlyDeleteCasesPermission, @@ -39,11 +40,56 @@ describe('Severity form field', () => { }); it('renders', async () => { - const result = appMockRender.render(); - expect(result.getByText('Showing 5 cases')).toBeInTheDocument(); - expect(result.getByText('Selected 1 case')).toBeInTheDocument(); - expect(result.getByTestId('case-table-bulk-actions-link-icon')).toBeInTheDocument(); - expect(result.getByTestId('all-cases-refresh-link-icon')).toBeInTheDocument(); + appMockRender.render(); + expect(screen.getByText('Showing 5 of 5 cases')).toBeInTheDocument(); + expect(screen.getByText('Selected 1 case')).toBeInTheDocument(); + expect(screen.getByTestId('case-table-bulk-actions-link-icon')).toBeInTheDocument(); + expect(screen.getByTestId('all-cases-refresh-link-icon')).toBeInTheDocument(); + expect(screen.queryByTestId('all-cases-maximum-limit-warning')).not.toBeInTheDocument(); + }); + + it('renders showing cases correctly', async () => { + const updatedProps = { + ...props, + totalCases: 20, + pagination: { + ...props.pagination, + totalItemCount: 20, + } + } + appMockRender.render(); + expect(screen.getByText('Showing 10 of 20 cases')).toBeInTheDocument(); + expect(screen.getByText('Selected 1 case')).toBeInTheDocument(); + }); + + it('renders showing cases correctly for second page', async () => { + const updatedProps = { + ...props, + totalCases: 20, + pagination: { + ...props.pagination, + pageIndex: 2, + totalItemCount: 20, + } + } + appMockRender.render(); + expect(screen.getByText('Showing 10 of 20 cases')).toBeInTheDocument(); + expect(screen.getByText('Selected 1 case')).toBeInTheDocument(); + }); + + it('renders showing cases correctly when no cases available', async () => { + const updatedProps = { + totalCases: 0, + selectedCases: [], + deselectCases, + pagination: { + pageSize: 10, + pageIndex: 1, + totalItemCount: 0, + } + } + appMockRender.render(); + expect(screen.getByText('Showing 0 of 0 cases')).toBeInTheDocument(); }); it('opens the bulk actions correctly', async () => { @@ -121,4 +167,54 @@ describe('Severity form field', () => { expect(result.queryByTestId('case-table-bulk-actions-link-icon')).toBeFalsy(); expect(result.queryByText('Showing 0 cases')).toBeFalsy(); }); + + describe('Maximum number of cases', () => { + const newProps = { + ...props, + selectedCaseS: [], + totalCases: MAX_DOCS_PER_PAGE, + pagination: { + ...props.pagination, + totalItemCount: MAX_DOCS_PER_PAGE, + } + }; + + const allCasesPageSize = [10,25,50,100]; + + it.each(allCasesPageSize)( + `does not show warning when totalCases = ${MAX_DOCS_PER_PAGE} but pageSize(%s) * pageIndex + 1 < ${MAX_DOCS_PER_PAGE}`, + (size) => { + const newPageIndex = (MAX_DOCS_PER_PAGE / size) - 2; + + console.log({size, newPageIndex}) + appMockRender.render(); + expect(screen.getByText(`Showing ${size} of ${MAX_DOCS_PER_PAGE} cases`)).toBeInTheDocument(); + expect(screen.queryByTestId('all-cases-maximum-limit-warning')).not.toBeInTheDocument(); + } + ); + + it.each(allCasesPageSize)( + `shows warning when totalCases = ${MAX_DOCS_PER_PAGE} but pageSize(%s) * pageIndex + 1 = ${MAX_DOCS_PER_PAGE}`, + (size) => { + const newPageIndex = (MAX_DOCS_PER_PAGE / size) - 1; + + console.log({size, newPageIndex}) + appMockRender.render(); + expect(screen.getByText(`Showing ${size} of ${MAX_DOCS_PER_PAGE} cases`)).toBeInTheDocument(); + expect(screen.getByTestId('all-cases-maximum-limit-warning')).toBeInTheDocument(); + } + ); + + it.each(allCasesPageSize)( + `shows warning when totalCases = ${MAX_DOCS_PER_PAGE} but pageSize(%s) * pageIndex + 1 > ${MAX_DOCS_PER_PAGE}`, + (size) => { + const newPageIndex = MAX_DOCS_PER_PAGE / size; + + console.log({size, newPageIndex}) + appMockRender.render(); + expect(screen.getByText(`Showing ${size} of ${MAX_DOCS_PER_PAGE} cases`)).toBeInTheDocument(); + expect(screen.getByTestId('all-cases-maximum-limit-warning')).toBeInTheDocument(); + } + ); + }); }); diff --git a/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx b/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx index 93b04f021edac..419a91f48c469 100644 --- a/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx @@ -76,11 +76,11 @@ export const CasesTableUtilityBar: FunctionComponent = React.memo( const visibleCases = totalCases > pagination.pageSize ? pagination.pageSize : totalCases; - const hasReachedMaxCases = totalCases > MAX_DOCS_PER_PAGE && (pagination.pageSize * (pagination.pageIndex + 1)) >= MAX_DOCS_PER_PAGE; + const hasReachedMaxCases = totalCases >= MAX_DOCS_PER_PAGE && (pagination.pageSize * (pagination.pageIndex + 1)) >= MAX_DOCS_PER_PAGE; const isDoNotShowAgainSelected = localStorageWarning && localStorageWarning === true; - const renderMaxLengthWarning = (): React.ReactNode => ( + const renderMaxLimitWarning = (): React.ReactNode => ( {i18n.MAX_CASES(totalCases)} @@ -181,10 +181,10 @@ export const CasesTableUtilityBar: FunctionComponent = React.memo( From 550065e581b32dc600fd72f5e979ea401255fd12 Mon Sep 17 00:00:00 2001 From: Janki Salvi <117571355+js-jankisalvi@users.noreply.github.com> Date: Mon, 21 Aug 2023 13:26:44 +0200 Subject: [PATCH 03/10] fix all cases unit tests --- .../cases/public/components/all_cases/all_cases_list.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx index 180a8828c88b1..450adc24140e8 100644 --- a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx @@ -191,7 +191,7 @@ describe('AllCasesListGeneric', () => { expect( screen.getAllByTestId('case-table-column-createdAt')[0].querySelector('.euiToolTipAnchor') ).toHaveTextContent(removeMsFromDate(useGetCasesMockState.data.cases[0].createdAt)); - expect(screen.getByTestId('case-table-case-count')).toHaveTextContent('Showing 10 cases'); + expect(screen.getByTestId('case-table-case-count')).toHaveTextContent(`Showing 10 of ${useGetCasesMockState.data.total} cases`); }); }); From 79f38649afdc74466b1958208324283d34c889db Mon Sep 17 00:00:00 2001 From: Janki Salvi <117571355+js-jankisalvi@users.noreply.github.com> Date: Mon, 21 Aug 2023 15:23:47 +0200 Subject: [PATCH 04/10] add tests for button actions --- .../all_cases/all_cases_list.test.tsx | 4 +- .../components/all_cases/translations.ts | 6 +- .../components/all_cases/utility_bar.test.tsx | 212 +++++++++++++----- .../components/all_cases/utility_bar.tsx | 72 ++++-- 4 files changed, 218 insertions(+), 76 deletions(-) diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx index 450adc24140e8..158f89ffeeb14 100644 --- a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx @@ -191,7 +191,9 @@ describe('AllCasesListGeneric', () => { expect( screen.getAllByTestId('case-table-column-createdAt')[0].querySelector('.euiToolTipAnchor') ).toHaveTextContent(removeMsFromDate(useGetCasesMockState.data.cases[0].createdAt)); - expect(screen.getByTestId('case-table-case-count')).toHaveTextContent(`Showing 10 of ${useGetCasesMockState.data.total} cases`); + expect(screen.getByTestId('case-table-case-count')).toHaveTextContent( + `Showing 10 of ${useGetCasesMockState.data.total} cases` + ); }); }); 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 8597eb96317d9..66b1e4aa4c84f 100644 --- a/x-pack/plugins/cases/public/components/all_cases/translations.ts +++ b/x-pack/plugins/cases/public/components/all_cases/translations.ts @@ -34,13 +34,15 @@ export const SHOWING_SELECTED_CASES = (totalRules: number) => export const SHOWING_CASES = (totalCases: number, pageSize: number) => i18n.translate('xpack.cases.caseTable.showingCasesTitle', { values: { totalCases, pageSize }, - defaultMessage: 'Showing {pageSize} of {totalCases} {totalCases, plural, =1 {case} other {cases}}', + defaultMessage: + 'Showing {pageSize} of {totalCases} {totalCases, plural, =1 {case} other {cases}}', }); export const MAX_CASES = (totalCases: number) => i18n.translate('xpack.cases.caseTable.maxCases', { values: { totalCases }, - defaultMessage: 'The results were capped at {totalCases} to maintain performance. Try limiting your search to reduce the results.', + defaultMessage: + 'The results were capped at {totalCases} to maintain performance. Try limiting your search to reduce the results.', }); export const DISMISS = i18n.translate('xpack.cases.caseTable.dismiss', { diff --git a/x-pack/plugins/cases/public/components/all_cases/utility_bar.test.tsx b/x-pack/plugins/cases/public/components/all_cases/utility_bar.test.tsx index ab18bf1870806..4d740c2fb9552 100644 --- a/x-pack/plugins/cases/public/components/all_cases/utility_bar.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/utility_bar.test.tsx @@ -23,6 +23,7 @@ import { CasesTableUtilityBar } from './utility_bar'; describe('Severity form field', () => { let appMockRender: AppMockRenderer; const deselectCases = jest.fn(); + const localStorageKey = 'cases.testAppId.utilityBar.hideMaxLimitWarning'; const props = { totalCases: 5, @@ -32,7 +33,7 @@ describe('Severity form field', () => { pageIndex: 1, pageSize: 10, totalItemCount: 5, - } + }, }; beforeEach(() => { @@ -55,8 +56,8 @@ describe('Severity form field', () => { pagination: { ...props.pagination, totalItemCount: 20, - } - } + }, + }; appMockRender.render(); expect(screen.getByText('Showing 10 of 20 cases')).toBeInTheDocument(); expect(screen.getByText('Selected 1 case')).toBeInTheDocument(); @@ -70,8 +71,8 @@ describe('Severity form field', () => { ...props.pagination, pageIndex: 2, totalItemCount: 20, - } - } + }, + }; appMockRender.render(); expect(screen.getByText('Showing 10 of 20 cases')).toBeInTheDocument(); expect(screen.getByText('Selected 1 case')).toBeInTheDocument(); @@ -86,51 +87,43 @@ describe('Severity form field', () => { pageSize: 10, pageIndex: 1, totalItemCount: 0, - } - } + }, + }; appMockRender.render(); expect(screen.getByText('Showing 0 of 0 cases')).toBeInTheDocument(); }); it('opens the bulk actions correctly', async () => { - const result = appMockRender.render(); + appMockRender.render(); - act(() => { - userEvent.click(result.getByTestId('case-table-bulk-actions-link-icon')); - }); + userEvent.click(screen.getByTestId('case-table-bulk-actions-link-icon')); await waitFor(() => { - expect(result.getByTestId('case-table-bulk-actions-context-menu')); + expect(screen.getByTestId('case-table-bulk-actions-context-menu')); }); }); it('closes the bulk actions correctly', async () => { - const result = appMockRender.render(); + appMockRender.render(); - act(() => { - userEvent.click(result.getByTestId('case-table-bulk-actions-link-icon')); - }); + userEvent.click(screen.getByTestId('case-table-bulk-actions-link-icon')); await waitFor(() => { - expect(result.getByTestId('case-table-bulk-actions-context-menu')); + expect(screen.getByTestId('case-table-bulk-actions-context-menu')); }); - act(() => { - userEvent.click(result.getByTestId('case-table-bulk-actions-link-icon')); - }); + userEvent.click(screen.getByTestId('case-table-bulk-actions-link-icon')); await waitFor(() => { - expect(result.queryByTestId('case-table-bulk-actions-context-menu')).toBeFalsy(); + expect(screen.queryByTestId('case-table-bulk-actions-context-menu')).toBeFalsy(); }); }); it('refresh correctly', async () => { - const result = appMockRender.render(); + appMockRender.render(); const queryClientSpy = jest.spyOn(appMockRender.queryClient, 'invalidateQueries'); - act(() => { - userEvent.click(result.getByTestId('all-cases-refresh-link-icon')); - }); + userEvent.click(screen.getByTestId('all-cases-refresh-link-icon')); await waitFor(() => { expect(deselectCases).toHaveBeenCalled(); @@ -142,30 +135,30 @@ describe('Severity form field', () => { it('does not show the bulk actions without update & delete permissions', async () => { appMockRender = createAppMockRenderer({ permissions: noCasesPermissions() }); - const result = appMockRender.render(); + appMockRender.render(); - expect(result.queryByTestId('case-table-bulk-actions-link-icon')).toBeFalsy(); + expect(screen.queryByTestId('case-table-bulk-actions-link-icon')).toBeFalsy(); }); it('does show the bulk actions with only delete permissions', async () => { appMockRender = createAppMockRenderer({ permissions: onlyDeleteCasesPermission() }); - const result = appMockRender.render(); + appMockRender.render(); - expect(result.getByTestId('case-table-bulk-actions-link-icon')).toBeInTheDocument(); + expect(screen.getByTestId('case-table-bulk-actions-link-icon')).toBeInTheDocument(); }); it('does show the bulk actions with update permissions', async () => { appMockRender = createAppMockRenderer({ permissions: writeCasesPermissions() }); - const result = appMockRender.render(); + appMockRender.render(); - expect(result.getByTestId('case-table-bulk-actions-link-icon')).toBeInTheDocument(); + expect(screen.getByTestId('case-table-bulk-actions-link-icon')).toBeInTheDocument(); }); it('does not show the bulk actions if there are not selected cases', async () => { - const result = appMockRender.render(); + appMockRender.render(); - expect(result.queryByTestId('case-table-bulk-actions-link-icon')).toBeFalsy(); - expect(result.queryByText('Showing 0 cases')).toBeFalsy(); + expect(screen.queryByTestId('case-table-bulk-actions-link-icon')).toBeFalsy(); + expect(screen.queryByText('Showing 0 cases')).toBeFalsy(); }); describe('Maximum number of cases', () => { @@ -174,21 +167,30 @@ describe('Severity form field', () => { selectedCaseS: [], totalCases: MAX_DOCS_PER_PAGE, pagination: { - ...props.pagination, + ...props.pagination, totalItemCount: MAX_DOCS_PER_PAGE, - } + }, }; - const allCasesPageSize = [10,25,50,100]; + const allCasesPageSize = [10, 25, 50, 100]; it.each(allCasesPageSize)( `does not show warning when totalCases = ${MAX_DOCS_PER_PAGE} but pageSize(%s) * pageIndex + 1 < ${MAX_DOCS_PER_PAGE}`, (size) => { - const newPageIndex = (MAX_DOCS_PER_PAGE / size) - 2; - - console.log({size, newPageIndex}) - appMockRender.render(); - expect(screen.getByText(`Showing ${size} of ${MAX_DOCS_PER_PAGE} cases`)).toBeInTheDocument(); + const newPageIndex = MAX_DOCS_PER_PAGE / size - 2; + + appMockRender.render( + + ); + + expect( + screen.getByText(`Showing ${size} of ${MAX_DOCS_PER_PAGE} cases`) + ).toBeInTheDocument(); expect(screen.queryByTestId('all-cases-maximum-limit-warning')).not.toBeInTheDocument(); } ); @@ -196,11 +198,20 @@ describe('Severity form field', () => { it.each(allCasesPageSize)( `shows warning when totalCases = ${MAX_DOCS_PER_PAGE} but pageSize(%s) * pageIndex + 1 = ${MAX_DOCS_PER_PAGE}`, (size) => { - const newPageIndex = (MAX_DOCS_PER_PAGE / size) - 1; - - console.log({size, newPageIndex}) - appMockRender.render(); - expect(screen.getByText(`Showing ${size} of ${MAX_DOCS_PER_PAGE} cases`)).toBeInTheDocument(); + const newPageIndex = MAX_DOCS_PER_PAGE / size - 1; + + appMockRender.render( + + ); + + expect( + screen.getByText(`Showing ${size} of ${MAX_DOCS_PER_PAGE} cases`) + ).toBeInTheDocument(); expect(screen.getByTestId('all-cases-maximum-limit-warning')).toBeInTheDocument(); } ); @@ -210,11 +221,112 @@ describe('Severity form field', () => { (size) => { const newPageIndex = MAX_DOCS_PER_PAGE / size; - console.log({size, newPageIndex}) - appMockRender.render(); - expect(screen.getByText(`Showing ${size} of ${MAX_DOCS_PER_PAGE} cases`)).toBeInTheDocument(); + appMockRender.render( + + ); + + expect( + screen.getByText(`Showing ${size} of ${MAX_DOCS_PER_PAGE} cases`) + ).toBeInTheDocument(); expect(screen.getByTestId('all-cases-maximum-limit-warning')).toBeInTheDocument(); } ); + + it('should show dismiss and do not show again buttons correctly', () => { + appMockRender.render( + + ); + + expect(screen.getByTestId('all-cases-maximum-limit-warning')).toBeInTheDocument(); + expect(screen.getByTestId('dismiss-warning')).toBeInTheDocument(); + + expect(screen.getByTestId('do-not-show-warning')).toBeInTheDocument(); + }); + + it('should dismiss warning correctly', () => { + appMockRender.render( + + ); + + expect(screen.getByTestId('all-cases-maximum-limit-warning')).toBeInTheDocument(); + expect(screen.getByTestId('dismiss-warning')).toBeInTheDocument(); + + userEvent.click(screen.getByTestId('dismiss-warning')); + + expect(screen.queryByTestId('all-cases-maximum-limit-warning')).not.toBeInTheDocument(); + }); + + describe('do not show button', () => { + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.clearAllTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + sessionStorage.removeItem(localStorageKey); + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should set storage key correctly', () => { + appMockRender.render( + + ); + + expect(screen.getByTestId('all-cases-maximum-limit-warning')).toBeInTheDocument(); + expect(screen.getByTestId('do-not-show-warning')).toBeInTheDocument(); + + expect(localStorage.getItem(localStorageKey)).toBe(null); + }); + + it('should hide warning correctly when do not show button clicked', () => { + appMockRender.render( + + ); + + expect(screen.getByTestId('all-cases-maximum-limit-warning')).toBeInTheDocument(); + expect(screen.getByTestId('do-not-show-warning')).toBeInTheDocument(); + + userEvent.click(screen.getByTestId('do-not-show-warning')); + + act(() => { + jest.advanceTimersByTime(1000); + }); + + expect(screen.queryByTestId('all-cases-maximum-limit-warning')).not.toBeInTheDocument(); + expect(localStorage.getItem(localStorageKey)).toBe('true'); + }); + }); }); }); diff --git a/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx b/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx index 419a91f48c469..eb84d51da8d5a 100644 --- a/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx @@ -9,6 +9,7 @@ import type { FunctionComponent } from 'react'; import { css } from '@emotion/react'; import useLocalStorage from 'react-use/lib/useLocalStorage'; import React, { useCallback, useState } from 'react'; +import type { Pagination } from '@elastic/eui'; import { EuiButtonEmpty, EuiContextMenu, @@ -17,7 +18,6 @@ import { EuiPopover, EuiText, useEuiTheme, - Pagination, EuiCallOut, EuiSpacer, } from '@elastic/eui'; @@ -50,7 +50,10 @@ export const CasesTableUtilityBar: FunctionComponent = React.memo( const togglePopover = useCallback(() => setIsPopoverOpen(!isPopoverOpen), [isPopoverOpen]); const closePopover = useCallback(() => setIsPopoverOpen(false), []); - const toggleWarning = useCallback(() => setIsMessageDismissed(!isMessageDismissed), [isMessageDismissed]); + const toggleWarning = useCallback( + () => setIsMessageDismissed(!isMessageDismissed), + [isMessageDismissed] + ); const onRefresh = useCallback(() => { deselectCases(); @@ -76,20 +79,44 @@ export const CasesTableUtilityBar: FunctionComponent = React.memo( const visibleCases = totalCases > pagination.pageSize ? pagination.pageSize : totalCases; - const hasReachedMaxCases = totalCases >= MAX_DOCS_PER_PAGE && (pagination.pageSize * (pagination.pageIndex + 1)) >= MAX_DOCS_PER_PAGE; + const hasReachedMaxCases = + totalCases >= MAX_DOCS_PER_PAGE && + pagination.pageSize * (pagination.pageIndex + 1) >= MAX_DOCS_PER_PAGE; const isDoNotShowAgainSelected = localStorageWarning && localStorageWarning === true; const renderMaxLimitWarning = (): React.ReactNode => ( - {i18n.MAX_CASES(totalCases)} + + {i18n.MAX_CASES(totalCases)} + - {i18n.DISMISS} + + {i18n.DISMISS} + - {i18n.NOT_SHOW_AGAIN} + + {i18n.NOT_SHOW_AGAIN} + ); @@ -174,23 +201,22 @@ export const CasesTableUtilityBar: FunctionComponent = React.memo( {modals} {flyouts} - {hasReachedMaxCases && !isMessageDismissed && !isDoNotShowAgainSelected - ? ( - <> - - - - - - - - - ) : null} + {hasReachedMaxCases && !isMessageDismissed && !isDoNotShowAgainSelected ? ( + <> + + + + + + + + + ) : null} ); } From 11fded9859737591024037ab3aa3e8634e80edd6 Mon Sep 17 00:00:00 2001 From: Janki Salvi <117571355+js-jankisalvi@users.noreply.github.com> Date: Wed, 23 Aug 2023 14:03:53 +0200 Subject: [PATCH 05/10] check pagesize --- .../plugins/cases/public/components/all_cases/utility_bar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx b/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx index eb84d51da8d5a..88805d028990e 100644 --- a/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx @@ -77,10 +77,10 @@ export const CasesTableUtilityBar: FunctionComponent = React.memo( */ const showBulkActions = (permissions.update || permissions.delete) && selectedCases.length > 0; - const visibleCases = totalCases > pagination.pageSize ? pagination.pageSize : totalCases; + const visibleCases = pagination?.pageSize && totalCases > pagination.pageSize ? pagination.pageSize : totalCases; const hasReachedMaxCases = - totalCases >= MAX_DOCS_PER_PAGE && + pagination.pageSize && totalCases >= MAX_DOCS_PER_PAGE && pagination.pageSize * (pagination.pageIndex + 1) >= MAX_DOCS_PER_PAGE; const isDoNotShowAgainSelected = localStorageWarning && localStorageWarning === true; From d1d349f59e129c86a4c63db58124d8adb7bd7ef0 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 23 Aug 2023 12:09:11 +0000 Subject: [PATCH 06/10] [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' --- .../cases/public/components/all_cases/utility_bar.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx b/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx index 88805d028990e..8a2565633aca0 100644 --- a/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx @@ -77,10 +77,12 @@ export const CasesTableUtilityBar: FunctionComponent = React.memo( */ const showBulkActions = (permissions.update || permissions.delete) && selectedCases.length > 0; - const visibleCases = pagination?.pageSize && totalCases > pagination.pageSize ? pagination.pageSize : totalCases; + const visibleCases = + pagination?.pageSize && totalCases > pagination.pageSize ? pagination.pageSize : totalCases; const hasReachedMaxCases = - pagination.pageSize && totalCases >= MAX_DOCS_PER_PAGE && + pagination.pageSize && + totalCases >= MAX_DOCS_PER_PAGE && pagination.pageSize * (pagination.pageIndex + 1) >= MAX_DOCS_PER_PAGE; const isDoNotShowAgainSelected = localStorageWarning && localStorageWarning === true; From 27aaaf97b4c401d600a4189de53ed76ef07ad067 Mon Sep 17 00:00:00 2001 From: Janki Salvi <117571355+js-jankisalvi@users.noreply.github.com> Date: Wed, 23 Aug 2023 14:11:10 +0200 Subject: [PATCH 07/10] add check in all_cases list --- .../cases/public/components/all_cases/all_cases_list.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx index 158f89ffeeb14..10b55c0e979b3 100644 --- a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx @@ -194,6 +194,7 @@ describe('AllCasesListGeneric', () => { expect(screen.getByTestId('case-table-case-count')).toHaveTextContent( `Showing 10 of ${useGetCasesMockState.data.total} cases` ); + expect(screen.queryByTestId('all-cases-maximum-limit-warning')).not.toBeInTheDocument(); }); }); From 878b0f224498eb70448f02fbd667600b14ef16f3 Mon Sep 17 00:00:00 2001 From: Janki Salvi <117571355+js-jankisalvi@users.noreply.github.com> Date: Wed, 23 Aug 2023 16:54:16 +0200 Subject: [PATCH 08/10] PR feedback --- .../public/components/all_cases/translations.ts | 14 +++++++------- .../public/components/all_cases/utility_bar.tsx | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) 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 66b1e4aa4c84f..14f15f8da6b6b 100644 --- a/x-pack/plugins/cases/public/components/all_cases/translations.ts +++ b/x-pack/plugins/cases/public/components/all_cases/translations.ts @@ -31,18 +31,18 @@ export const SHOWING_SELECTED_CASES = (totalRules: number) => defaultMessage: 'Selected {totalRules} {totalRules, plural, =1 {case} other {cases}}', }); -export const SHOWING_CASES = (totalCases: number, pageSize: number) => - i18n.translate('xpack.cases.caseTable.showingCasesTitle', { - values: { totalCases, pageSize }, +export const SHOWING_CASES = (totalRules: number, pageSize: number) => + i18n.translate('xpack.cases.caseTable.showingCases', { + values: { totalRules, pageSize }, defaultMessage: - 'Showing {pageSize} of {totalCases} {totalCases, plural, =1 {case} other {cases}}', + 'Showing {pageSize} of {totalRules} {totalRules, plural, =1 {case} other {cases}}', }); -export const MAX_CASES = (totalCases: number) => +export const MAX_CASES = (maxCases: number) => i18n.translate('xpack.cases.caseTable.maxCases', { - values: { totalCases }, + values: { maxCases }, defaultMessage: - 'The results were capped at {totalCases} to maintain performance. Try limiting your search to reduce the results.', + 'The results were capped at {maxCases} to maintain performance. Try limiting your search to reduce the results.', }); export const DISMISS = i18n.translate('xpack.cases.caseTable.dismiss', { diff --git a/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx b/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx index 8a2565633aca0..2c2aebdb30bb2 100644 --- a/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/utility_bar.tsx @@ -97,7 +97,7 @@ export const CasesTableUtilityBar: FunctionComponent = React.memo( margin-top: 4px; `} > - {i18n.MAX_CASES(totalCases)} + {i18n.MAX_CASES(MAX_DOCS_PER_PAGE)} @@ -203,7 +203,7 @@ export const CasesTableUtilityBar: FunctionComponent = React.memo( {modals} {flyouts} - {hasReachedMaxCases && !isMessageDismissed && !isDoNotShowAgainSelected ? ( + {hasReachedMaxCases && !isMessageDismissed && !isDoNotShowAgainSelected && ( <> @@ -218,7 +218,7 @@ export const CasesTableUtilityBar: FunctionComponent = React.memo( - ) : null} + )} ); } From 6c646d7b5852708753b594cf065bff7d6767a80d Mon Sep 17 00:00:00 2001 From: Janki Salvi <117571355+js-jankisalvi@users.noreply.github.com> Date: Wed, 23 Aug 2023 18:01:36 +0200 Subject: [PATCH 09/10] fix the translation title --- .../plugins/cases/public/components/all_cases/translations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 14f15f8da6b6b..4c96c351d5ac7 100644 --- a/x-pack/plugins/cases/public/components/all_cases/translations.ts +++ b/x-pack/plugins/cases/public/components/all_cases/translations.ts @@ -32,7 +32,7 @@ export const SHOWING_SELECTED_CASES = (totalRules: number) => }); export const SHOWING_CASES = (totalRules: number, pageSize: number) => - i18n.translate('xpack.cases.caseTable.showingCases', { + i18n.translate('xpack.cases.caseTable.showingCasesTitle', { values: { totalRules, pageSize }, defaultMessage: 'Showing {pageSize} of {totalRules} {totalRules, plural, =1 {case} other {cases}}', From b728fabddb3e912c6474b3814e66975900774fe8 Mon Sep 17 00:00:00 2001 From: Janki Salvi <117571355+js-jankisalvi@users.noreply.github.com> Date: Thu, 24 Aug 2023 09:53:29 +0200 Subject: [PATCH 10/10] Fix translation check --- x-pack/plugins/translations/translations/fr-FR.json | 1 - x-pack/plugins/translations/translations/ja-JP.json | 1 - x-pack/plugins/translations/translations/zh-CN.json | 1 - 3 files changed, 3 deletions(-) diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index e125ad908942f..fa46583300b92 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -10462,7 +10462,6 @@ "xpack.cases.caseTable.caseDetailsLinkAria": "cliquez pour visiter le cas portant le titre {detailName}", "xpack.cases.caseTable.pushLinkAria": "cliquez pour afficher l'incident relatif à {thirdPartyName}.", "xpack.cases.caseTable.selectedCasesTitle": "{totalRules} {totalRules, plural, =1 {cas} one {aux cas suivants} many {cas} other {cas}} sélectionné(s)", - "xpack.cases.caseTable.showingCasesTitle": "Affichage de {totalRules} {totalRules, plural, =1 {cas} one {aux cas suivants} many {cas} other {cas}}", "xpack.cases.caseTable.unit": "{totalCount, plural, =1 {cas} one {aux cas suivants} many {cas} other {cas}}", "xpack.cases.caseView.actionLabel.selectedThirdParty": "{thirdParty} sélectionné comme système de gestion des incidents", "xpack.cases.caseView.actionLabel.viewIncident": "Afficher {incidentNumber}", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 11be15c392ef4..ca0918a710e07 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -10477,7 +10477,6 @@ "xpack.cases.caseTable.caseDetailsLinkAria": "クリックすると、タイトル{detailName}のケースを表示します", "xpack.cases.caseTable.pushLinkAria": "クリックすると、{thirdPartyName}でインシデントを表示します。", "xpack.cases.caseTable.selectedCasesTitle": "{totalRules}件の{totalRules, plural, =1 {ケース} other {ケース}}が選択済み", - "xpack.cases.caseTable.showingCasesTitle": "{totalRules} {totalRules, plural, =1 {ケース} other {ケース}}を表示中", "xpack.cases.caseTable.unit": "{totalCount, plural, =1 {ケース} other {ケース}}", "xpack.cases.caseView.actionLabel.selectedThirdParty": "{thirdParty}をインシデント管理システムとして選択しました", "xpack.cases.caseView.actionLabel.viewIncident": "{incidentNumber}を表示", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 013f01bd36f54..192f11122e88d 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -10477,7 +10477,6 @@ "xpack.cases.caseTable.caseDetailsLinkAria": "单击以访问标题为 {detailName} 的案例", "xpack.cases.caseTable.pushLinkAria": "单击可在 {thirdPartyName} 上查看该事件。", "xpack.cases.caseTable.selectedCasesTitle": "已选定 {totalRules} 个{totalRules, plural, =1 {案例} other {案例}}", - "xpack.cases.caseTable.showingCasesTitle": "正在显示 {totalRules} 个 {totalRules, plural, =1 {案例} other {案例}}", "xpack.cases.caseTable.unit": "{totalCount, plural, =1 {案例} other {案例}}", "xpack.cases.caseView.actionLabel.selectedThirdParty": "已选择 {thirdParty} 作为事件管理系统", "xpack.cases.caseView.actionLabel.viewIncident": "查看 {incidentNumber}",