[Security Solution] [Field Browser] Prevent pagination reset on field selection (#131714)

* prevent pagination reset on selection

* sorting controls

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Sergi Massaneda 2022-05-10 15:35:45 +02:00 committed by GitHub
parent 57597f7617
commit 818f5e63b2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 167 additions and 43 deletions

View file

@ -6,7 +6,7 @@
*/
import React from 'react';
import { render } from '@testing-library/react';
import { render, RenderResult } from '@testing-library/react';
import { mockBrowserFields, TestProviders } from '../../../../mock';
import { tGridActions } from '../../../../store/t_grid';
import { defaultColumnHeaderType } from '../../body/column_headers/default_headers';
@ -155,50 +155,117 @@ describe('FieldTable', () => {
expect(checkbox).toHaveAttribute('checked');
});
it('should dispatch remove column action on field unchecked', () => {
const result = render(
<TestProviders>
<FieldTable
{...defaultProps}
selectedCategoryIds={['base']}
columnHeaders={columnHeaders}
filteredBrowserFields={{ base: { fields: { [timestampFieldId]: timestampField } } }}
/>
</TestProviders>
);
describe('selection', () => {
it('should dispatch remove column action on field unchecked', () => {
const result = render(
<TestProviders>
<FieldTable
{...defaultProps}
selectedCategoryIds={['base']}
columnHeaders={columnHeaders}
filteredBrowserFields={{ base: { fields: { [timestampFieldId]: timestampField } } }}
/>
</TestProviders>
);
result.getByTestId(`field-${timestampFieldId}-checkbox`).click();
result.getByTestId(`field-${timestampFieldId}-checkbox`).click();
expect(mockDispatch).toHaveBeenCalledTimes(1);
expect(mockDispatch).toHaveBeenCalledWith(
tGridActions.removeColumn({ id: timelineId, columnId: timestampFieldId })
);
expect(mockDispatch).toHaveBeenCalledTimes(1);
expect(mockDispatch).toHaveBeenCalledWith(
tGridActions.removeColumn({ id: timelineId, columnId: timestampFieldId })
);
});
it('should dispatch upsert column action on field checked', () => {
const result = render(
<TestProviders>
<FieldTable
{...defaultProps}
selectedCategoryIds={['base']}
filteredBrowserFields={{ base: { fields: { [timestampFieldId]: timestampField } } }}
/>
</TestProviders>
);
result.getByTestId(`field-${timestampFieldId}-checkbox`).click();
expect(mockDispatch).toHaveBeenCalledTimes(1);
expect(mockDispatch).toHaveBeenCalledWith(
tGridActions.upsertColumn({
id: timelineId,
column: {
columnHeaderType: defaultColumnHeaderType,
id: timestampFieldId,
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
index: 1,
})
);
});
});
it('should dispatch upsert column action on field checked', () => {
const result = render(
<TestProviders>
describe('pagination', () => {
const isAtFirstPage = (result: RenderResult) =>
result.getByTestId('pagination-button-0').classList.contains('euiPaginationButton-isActive');
const changePage = (result: RenderResult) => {
result.getByTestId('pagination-button-1').click();
};
const defaultPaginationProps: FieldTableProps = {
...defaultProps,
filteredBrowserFields: mockBrowserFields,
};
it('should paginate on page clicked', () => {
const result = render(
<TestProviders>
<FieldTable {...defaultPaginationProps} />
</TestProviders>
);
expect(isAtFirstPage(result)).toBeTruthy();
changePage(result);
expect(isAtFirstPage(result)).toBeFalsy();
});
it('should not reset on field checked', () => {
const result = render(
<TestProviders>
<FieldTable {...defaultPaginationProps} />
</TestProviders>
);
changePage(result);
result.getAllByRole('checkbox').at(0)?.click();
expect(mockDispatch).toHaveBeenCalled(); // assert some field has been selected
expect(isAtFirstPage(result)).toBeFalsy();
});
it('should reset on filter change', () => {
const result = render(
<FieldTable
{...defaultProps}
selectedCategoryIds={['base']}
filteredBrowserFields={{ base: { fields: { [timestampFieldId]: timestampField } } }}
{...defaultPaginationProps}
selectedCategoryIds={['destination', 'event', 'client', 'agent', 'host']}
/>,
{ wrapper: TestProviders }
);
changePage(result);
expect(isAtFirstPage(result)).toBeFalsy();
result.rerender(
<FieldTable
{...defaultPaginationProps}
selectedCategoryIds={['destination', 'event', 'client', 'agent']}
/>
</TestProviders>
);
);
result.getByTestId(`field-${timestampFieldId}-checkbox`).click();
expect(mockDispatch).toHaveBeenCalledTimes(1);
expect(mockDispatch).toHaveBeenCalledWith(
tGridActions.upsertColumn({
id: timelineId,
column: {
columnHeaderType: defaultColumnHeaderType,
id: timestampFieldId,
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
},
index: 1,
})
);
expect(isAtFirstPage(result)).toBeTruthy();
});
});
});

View file

@ -5,9 +5,9 @@
* 2.0.
*/
import React, { useCallback, useMemo } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import styled from 'styled-components';
import { EuiInMemoryTable } from '@elastic/eui';
import { EuiInMemoryTable, Pagination, Direction } from '@elastic/eui';
import { useDispatch } from 'react-redux';
import { BrowserFields, ColumnHeaderOptions } from '../../../../../common';
import { getColumnHeader, getFieldColumns, getFieldItems, isActionsColumn } from './field_items';
@ -16,6 +16,11 @@ import { tGridActions } from '../../../../store/t_grid';
import type { GetFieldTableColumns } from '../../../../../common/types/fields_browser';
import { FieldTableHeader } from './field_table_header';
const DEFAULT_SORTING: { field: string; direction: Direction } = {
field: '',
direction: 'asc',
} as const;
export interface FieldTableProps {
timelineId: string;
columnHeaders: ColumnHeaderOptions[];
@ -69,6 +74,12 @@ const FieldTableComponent: React.FC<FieldTableProps> = ({
timelineId,
onHide,
}) => {
const [pageIndex, setPageIndex] = useState(0);
const [pageSize, setPageSize] = useState(10);
const [sortField, setSortField] = useState<string>(DEFAULT_SORTING.field);
const [sortDirection, setSortDirection] = useState<Direction>(DEFAULT_SORTING.direction);
const dispatch = useDispatch();
const fieldItems = useMemo(
@ -103,6 +114,51 @@ const FieldTableComponent: React.FC<FieldTableProps> = ({
[columnHeaders, dispatch, timelineId]
);
/**
* Pagination controls
*/
const pagination: Pagination = useMemo(
() => ({
pageIndex,
pageSize,
totalItemCount: fieldItems.length,
pageSizeOptions: [10, 25, 50],
}),
[fieldItems.length, pageIndex, pageSize]
);
useEffect(() => {
// Resets the pagination when some filter has changed, consequently, the number of fields is different
setPageIndex(0);
}, [fieldItems.length]);
/**
* Sorting controls
*/
const sorting = useMemo(
() => ({
sort: {
field: sortField,
direction: sortDirection,
},
}),
[sortDirection, sortField]
);
const onTableChange = useCallback(({ page, sort = DEFAULT_SORTING }) => {
const { index, size } = page;
const { field, direction } = sort;
setPageIndex(index);
setPageSize(size);
setSortField(field);
setSortDirection(direction);
}, []);
/**
* Process columns
*/
const columns = useMemo(
() => getFieldColumns({ highlight: searchInput, onToggleColumn, getFieldTableColumns, onHide }),
[onToggleColumn, searchInput, getFieldTableColumns, onHide]
@ -124,9 +180,10 @@ const FieldTableComponent: React.FC<FieldTableProps> = ({
items={fieldItems}
itemId="name"
columns={columns}
pagination={true}
sorting={true}
pagination={pagination}
sorting={sorting}
hasActions={hasActions}
onChange={onTableChange}
compressed
/>
</TableContainer>