Skip to content

Commit

Permalink
Merge pull request #97 from indec-it/feat/addSortOnTable
Browse files Browse the repository at this point in the history
feat: add sort on table
  • Loading branch information
maximilianoforlenza authored Jul 28, 2022
2 parents b315391 + f189030 commit f3e453b
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 35 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ lib/
.env-*
.idea
*.log
.history/
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@indec/react-commons",
"version": "5.4.2",
"version": "5.5.0",
"description": "Common reactjs components for apps",
"private": false,
"main": "index.js",
Expand Down
30 changes: 30 additions & 0 deletions src/components/Table/SortIcon/SortIcon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import {sortDirections} from '@/constants';
import {Icon} from '@chakra-ui/react';
import {FaChevronDown, FaChevronUp} from 'react-icons/fa';
import PropTypes from 'prop-types';

const SortIcon = ({classified, columnKey, ...props}) => classified && classified?.sort === columnKey && (
<Icon
as={classified.sortBy === sortDirections.ASC ? FaChevronUp : FaChevronDown}
color="brand.neutral300"
w={4}
h={4}
{...props}
/>
);

SortIcon.propTypes = {
classified: PropTypes.shape({
sortBy: PropTypes.oneOf([sortDirections.ASC, sortDirections.DESC]),
sort: PropTypes.string
}),
columnKey: PropTypes.string
};

SortIcon.defaultProps = {
classified: undefined,
columnKey: undefined
};

export default SortIcon;
3 changes: 3 additions & 0 deletions src/components/Table/SortIcon/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import SortIcon from './SortIcon';

export default SortIcon;
93 changes: 63 additions & 30 deletions src/components/Table/Table.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {isValidElement} from 'react';
import React, {useState, isValidElement} from 'react';
import PropTypes from 'prop-types';
import {
Flex,
Expand All @@ -9,32 +9,55 @@ import {
Thead,
Tr,
VStack,
Table as ChakraTable
Table as ChakraTable,
HStack,
Text
} from '@chakra-ui/react';

import {sortDirections} from '@/constants';
import {LoadingPage, Pagination} from '@/components';
import {buildRows} from '@/utils';
import TableFooter from '@/components/Table/TableFooter';
import SortIcon from '@/components/Table/SortIcon';

const Table = ({
name,
caption,
columns,
data,
caption,
isLoading,
emptyMessage,
total,
showDefaultFooter,
perPage,
footer: Footer,
isLoading,
name,
onSearch,
onSort,
paginationStyles,
params,
footer: Footer,
perPage,
showDefaultFooter,
showPagination,
paginationStyles,
total,
...props
}) => {
const columnsData = Array.isArray(columns) ? columns : [];
const sizeHeader = columnsData.length;
const initialClassified = {sort: null, sortBy: null};
const [classified, setClassified] = useState(initialClassified);

const handleSort = ({target: {id}}) => {
if (!id) {
return;
}
let sort;
if (id === 'action') {
sort = initialClassified;
} else if (classified?.sort === id && classified?.sortBy === sortDirections.ASC) {
sort = {sort: id, sortBy: sortDirections.DESC};
} else {
sort = {sort: id, sortBy: sortDirections.ASC};
}
setClassified(sort);
onSort(sort);
};

return (
<VStack w="100%">
Expand All @@ -56,10 +79,20 @@ const Table = ({
<Th
data-testid={`column-${column.key}`}
key={column.key}
id={column.key}
onClick={onSort ? handleSort : undefined}
cursor={onSort ? 'pointer' : 'initial'}
{...column.style}
>
{column.label}
<HStack>
{data.length > 0 && <SortIcon classified={classified} columnKey={column.key}/>}
<Text
ml="0 !important"
id={column.key}
data-testid={`column-text-${column.key}`}
>
{column.label || ''}
</Text>
</HStack>
</Th>
))}
</Tr>
Expand Down Expand Up @@ -107,38 +140,38 @@ const Table = ({
};

Table.propTypes = {
onSearch: PropTypes.func,
caption: PropTypes.string,
columns: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
params: PropTypes.shape({
skip: PropTypes.number
}),
name: PropTypes.string,
data: PropTypes.arrayOf(PropTypes.shape({})),
caption: PropTypes.string,
isLoading: PropTypes.bool,
showDefaultFooter: PropTypes.bool,
emptyMessage: PropTypes.string,
total: PropTypes.number,
footer: PropTypes.element,
isLoading: PropTypes.bool,
name: PropTypes.string,
onSearch: PropTypes.func,
onSort: PropTypes.func,
paginationStyles: PropTypes.shape({}),
params: PropTypes.shape({skip: PropTypes.number}),
perPage: PropTypes.number,
showDefaultFooter: PropTypes.bool,
showPagination: PropTypes.bool,
paginationStyles: PropTypes.shape({})
total: PropTypes.number
};

Table.defaultProps = {
name: 'table',
onSearch: () => {},
caption: null,
data: [],
footer: undefined,
emptyMessage: 'No hay resultados',
isLoading: false,
params: undefined,
name: 'table',
onSearch: () => {},
onSort: undefined,
paginationStyles: undefined,
params: undefined,
perPage: 0,
showDefaultFooter: true,
showPagination: true,
data: [],
total: 0,
perPage: 0,
footer: undefined,
emptyMessage: 'No hay resultados'
total: 0
};

export default Table;
20 changes: 17 additions & 3 deletions src/components/Table/Table.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,42 @@ const getRows = () => users.map(user => {
user.documentId,
user.role,
user.state,
user.deleted
user.status
];
return ({key: user.id, values: rows});
});

export const Basic = args => {
const spliceRows = getRows().slice(0, 5);
const rows = getRows();
const [prevArgs, updateArgs] = useArgs();
const handleSearch = ({target: {id, value}}) => updateArgs(
{...prevArgs, params: {...prevArgs.params, [id]: value}}
);
const handleSort = ({sort, sortBy}) => {
const orderedData = rows.sort((firstRow, secondRow) => {
const selectedColumn = columns.findIndex(column => column.key === sort);
if (firstRow.values[selectedColumn] < secondRow.values[selectedColumn]) {
return sortBy === 'asc' ? -1 : 1;
}
if (firstRow.values[selectedColumn] > secondRow.values[selectedColumn]) {
return sortBy === 'asc' ? 1 : -1;
}
return 0;
});
updateArgs({...prevArgs, data: orderedData});
};
return (
<VStack w="100%">
<Table
name="users"
columns={columns}
data={spliceRows}
data={rows}
isLoading={false}
caption="Users"
perPage={5}
total={users.length}
onSearch={handleSearch}
onSort={handleSort}
params={args.params}
variant="reg"
size="sm"
Expand Down
43 changes: 42 additions & 1 deletion src/components/Table/Table.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import {
getByTestId,
getByText,
queryByText,
queryByTestId
queryByTestId,
fireEvent
} from '@testing-library/react';

import {Table} from '@/components';
Expand Down Expand Up @@ -62,6 +63,46 @@ describe('<Table>', () => {
const {container} = getComponent();
expect(queryByText(container, 'No hay resultados')).toBeNull();
});

describe('when one column is clicked and is not an action', () => {
beforeEach(() => {
props.onSort = jest.fn();
const {container} = getComponent();
const firstColumn = getByTestId(container, 'column-text-name');
fireEvent.click(firstColumn);
});

it('should fire `props.onSort` in asc order', () => {
expect(props.onSort).toHaveBeenCalledTimes(1);
expect(props.onSort).toHaveBeenCalledWith({sort: 'name', sortBy: 'asc'});
});

describe('when the same column is clicked again', () => {
beforeEach(() => {
const {container} = getComponent();
const firstColumn = getByTestId(container, 'column-text-name');
fireEvent.click(firstColumn);
});

it('should fire `props.onSort` in desc order', () => {
expect(props.onSort).toHaveBeenCalledWith({sort: 'name', sortBy: 'desc'});
});
});
});

describe('when one column is clicked and is an action', () => {
beforeEach(() => {
props.onSort = jest.fn();
const {container} = getComponent();
const secondColumn = getByTestId(container, 'column-text-action');
fireEvent.click(secondColumn);
});

it('should fire `props.onSort` with sort and sortBy in null', () => {
expect(props.onSort).toHaveBeenCalledTimes(1);
expect(props.onSort).toHaveBeenCalledWith({sort: null, sortBy: null});
});
});
});

describe('when `props.caption` is defined', () => {
Expand Down
1 change: 1 addition & 0 deletions src/constants/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export {default as headerOptions} from './headerOptions';
export {default as selectActions} from './selectActions';
export {default as sortDirections} from './sortDirections';
export {default as users} from './users';
4 changes: 4 additions & 0 deletions src/constants/sortDirections.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default {
ASC: 'asc',
DESC: 'desc'
};

0 comments on commit f3e453b

Please sign in to comment.