Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/common/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export interface Page {
page: number;
perPage: number;
}

export interface SortBy {
index: number;
direction: 'asc' | 'desc';
}
1 change: 1 addition & 0 deletions src/hooks/useTable/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { useTable } from './useTable';
144 changes: 144 additions & 0 deletions src/hooks/useTable/useTable.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { renderHook } from '@testing-library/react-hooks';
import { Page } from '../../common/types';
import { useTable } from './useTable';

describe('useTableFilter', () => {
it('Pagination', () => {
const items = [...Array(15)].map((_, index) => index + 1);
const page: Page = { page: 1, perPage: 10 };

const { result } = renderHook(() =>
useTable<number>({
items: items,
currentPage: page,
filterItem: () => true,
compareToByColumn: () => 1,
})
);

// Page1
expect(result.current.pageItems).toEqual(items.slice(0, 10));
});

it('Filter', () => {
const items = [...Array(15)].map((_, index) => index + 1);
const page: Page = { page: 1, perPage: 10 };

const { result } = renderHook(() =>
useTable<number>({
items: items,
currentPage: page,
filterItem: (value) => value % 2 === 1,
compareToByColumn: () => 1,
})
);

// Page1
const expectedResult = [1, 3, 5, 7, 9, 11, 13, 15];
expect(result.current.pageItems).toEqual(expectedResult);
});

it('SortBy', () => {
const items = [...Array(15)].map((_, index) => index + 1);
const page: Page = { page: 1, perPage: 10 };
const filterItem = () => true;
const compareToByColumn = (a: number, b: number, indexCol?: number) =>
indexCol === 7 ? a - b : 0;

// Verify asc
const { result: resultAsc } = renderHook(() =>
useTable<number>({
items: items,
currentPage: page,
filterItem: filterItem,
currentSortBy: { direction: 'asc', index: 7 },
compareToByColumn: compareToByColumn,
})
);

const expectedAscResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
expect(resultAsc.current.pageItems).toEqual(expectedAscResult);

// Verify desc
const { result: resultDesc } = renderHook(() =>
useTable<number>({
items: items,
currentPage: page,
filterItem: filterItem,
currentSortBy: { direction: 'desc', index: 7 },
compareToByColumn: compareToByColumn,
})
);

const expectedDescResult = [15, 14, 13, 12, 11, 10, 9, 8, 7, 6];
expect(resultDesc.current.pageItems).toEqual(expectedDescResult);
});

it("SortBy when 'compareToByColumn' return always 0", () => {
const items = [...Array(15)].map((_, index) => index + 1);
const page: Page = { page: 1, perPage: 10 };

const expectedResult = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// Verify asc
const { result: resultAsc } = renderHook(() =>
useTable<number>({
items: items,
currentPage: page,
filterItem: () => true,
currentSortBy: { direction: 'asc', index: 7 },
compareToByColumn: () => 0, // forcing comparison true
})
);

expect(resultAsc.current.pageItems).toEqual(expectedResult);

// Verify desc
const { result: resultDesc } = renderHook(() =>
useTable<number>({
items: items,
currentPage: page,
filterItem: () => true,
currentSortBy: { direction: 'desc', index: 7 },
compareToByColumn: () => 0, // forcing comparison true
})
);

expect(resultDesc.current.pageItems).toEqual(expectedResult);
});

it("SortBy when 'compareToByColumn' return always 0 and 'filter' is applied", () => {
const items = [...Array(25)].map((_, index) => index + 1);
const page: Page = { page: 1, perPage: 10 };
const filterItem = (val: number) => val % 2 === 0;
const compareToByColumn = () => 0; // forcing comparison true

const expectedResult = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20];

// Verify asc
const { result: resultAsc } = renderHook(() =>
useTable<number>({
items: items,
currentPage: page,
filterItem: filterItem,
currentSortBy: { direction: 'asc', index: 7 },
compareToByColumn: compareToByColumn,
})
);

expect(resultAsc.current.pageItems).toEqual(expectedResult);

// Verify desc
const { result: resultDesc } = renderHook(() =>
useTable<number>({
items: items,
currentPage: page,
filterItem: filterItem,
currentSortBy: { direction: 'desc', index: 7 },
compareToByColumn: compareToByColumn,
})
);

expect(resultDesc.current.pageItems).toEqual(expectedResult);
});
});
64 changes: 64 additions & 0 deletions src/hooks/useTable/useTable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { useMemo } from 'react';
import { SortByDirection } from '@patternfly/react-table';
import { Page, SortBy } from '../../common/types';

// Hook

interface HookArgs<T> {
items?: T[];

currentSortBy?: SortBy;
compareToByColumn: (a: T, b: T, columnIndex?: number) => number;

currentPage: Page;
filterItem: (value: T) => boolean;
}

interface HookState<T> {
pageItems: T[];
filteredItems: T[];
}

export const useTable = <T>({
items,
currentSortBy,
currentPage,
filterItem,
compareToByColumn,
}: HookArgs<T>): HookState<T> => {
const state: HookState<T> = useMemo(() => {
const allItems = [...(items || [])];

// Filter
const filteredItems = allItems.filter(filterItem);

// Sort
let orderChanged = false;

let sortedItems: T[];
sortedItems = [...filteredItems].sort((a, b) => {
const comparisonResult = compareToByColumn(a, b, currentSortBy?.index);
if (comparisonResult !== 0) {
orderChanged = true;
}
return comparisonResult;
});

if (orderChanged && currentSortBy?.direction === SortByDirection.desc) {
sortedItems = sortedItems.reverse();
}

// Paginate
const pageItems = sortedItems.slice(
(currentPage.page - 1) * currentPage.perPage,
currentPage.page * currentPage.perPage
);

return {
pageItems,
filteredItems,
};
}, [items, currentPage, currentSortBy, compareToByColumn, filterItem]);

return state;
};
11 changes: 1 addition & 10 deletions src/hooks/useTableControls/useTableControls.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
import { useCallback, useReducer } from 'react';
import { ActionType, createAction, getType } from 'typesafe-actions';
import { IExtraColumnData, SortByDirection } from '@patternfly/react-table';

export interface Page {
page: number;
perPage: number;
}

export interface SortBy {
index: number;
direction: 'asc' | 'desc';
}
import { Page, SortBy } from '../../common/types';

// Actions

Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ export * from './components/StatusIcon';

export * from './hooks/useDelete';
export * from './hooks/useModal';
export * from './hooks/useTable';
export * from './hooks/useTableControls';
export * from './hooks/useToolbar';