Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(react-grid): Ability to show all rows on a page via page size selector #150

Merged
merged 19 commits into from
Jun 21, 2017
Merged
Show file tree
Hide file tree
Changes from 16 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
1 change: 1 addition & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ gulp.task('site:docs', function() {
'packages/**/*.md',
'!packages/**/LICENSE.md',
'!packages/dx-react-demos/**/*',
'!packages/dx-testing/**/*',
'!/**/node_modules/**/*'
])
.pipe(rename(function(path) {
Expand Down
28 changes: 22 additions & 6 deletions packages/dx-grid-core/src/plugins/paging-state/computeds.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
export const paginate = (rows, pageSize, page) =>
rows.slice(pageSize * page, pageSize * (page + 1));
export const paginate = (rows, pageSize, page) => (
pageSize ?
rows.slice(pageSize * page, pageSize * (page + 1)) :
rows
);

export const ensurePageHeaders = (rows, pageSize) => {
if (!pageSize) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why could we not escape earlier? Slicing seems redundant

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slicing is for array copying, isn't it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but this copy is needed only for processing listed below in this function. We can return original rows if processing is not needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, got it.

return rows;
}

const result = rows.slice();

const headers = [];
Expand All @@ -27,13 +34,22 @@ export const ensurePageHeaders = (rows, pageSize) => {
return result;
};

export const totalPageCount = (rows, pageSize) => Math.ceil(rows.length / pageSize);
export const totalPageCount = (rows, pageSize) => (
pageSize ? Math.ceil(rows.length / pageSize) : 1
);

export const totalCount = rows => rows.length;

export const firstRowOnPage = (currentPage, pageSize) => (currentPage * pageSize) + 1;
export const firstRowOnPage = (currentPage, pageSize) => (
pageSize ? (currentPage * pageSize) + 1 : 1
);

export const lastRowOnPage = (currentPage, pageSize, totalRowCount) => {
const index = (currentPage + 1) * pageSize;
return index > totalRowCount ? totalRowCount : index;
let result = totalRowCount;
if (pageSize) {
const index = (currentPage + 1) * pageSize;
result = index > totalRowCount ? totalRowCount : index;
}

return result;
};
34 changes: 31 additions & 3 deletions packages/dx-grid-core/src/plugins/paging-state/computeds.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ describe('PagingState computeds', () => {

page = paginate(rows, 2, 3);
expect(page).toEqual([]);

page = paginate(rows, 0, 1);
expect(page).toEqual(rows);
});
});

Expand Down Expand Up @@ -141,12 +144,31 @@ describe('PagingState computeds', () => {
expect(computedRows[3]).toBe(rows[3]);
expect(computedRows[4]).toBe(rows[4]);
});

it('should work if pageSize is \'all\'', () => {
const rows = [
{ a: 1, _headerKey: 'a' },
{ a: 2 },
{ a: 3 },
{ a: 4 },
];

const computedRows = ensurePageHeaders(rows, 0);
expect(computedRows).toHaveLength(4);
expect(computedRows[0]).toBe(rows[0]);
expect(computedRows[1]).toBe(rows[1]);
expect(computedRows[2]).toBe(rows[2]);
expect(computedRows[3]).toBe(rows[3]);
});
});

describe('#totalPageCount', () => {
it('should work', () => {
const count = totalPageCount([1, 2, 3], 2);
let count = totalPageCount([1, 2, 3], 2);
expect(count).toEqual(2);

count = totalPageCount([1, 2, 3], 0);
expect(count).toEqual(1);
});
});

Expand All @@ -159,15 +181,21 @@ describe('PagingState computeds', () => {

describe('#firstRowOnPage', () => {
it('should work', () => {
const count = firstRowOnPage(1, 5);
let count = firstRowOnPage(1, 5);
expect(count).toEqual(6);

count = firstRowOnPage(1, 0);
expect(count).toEqual(1);
});
});

describe('#lastRowOnPage', () => {
it('should work', () => {
const count = lastRowOnPage(1, 5, 15);
let count = lastRowOnPage(1, 5, 15);
expect(count).toEqual(10);

count = lastRowOnPage(1, 0, 15);
expect(count).toEqual(15);
});

it('should not be greater than total count', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class PageSizeSelectorDemo extends React.PureComponent {
{ name: 'car', title: 'Car' },
],
rows: generateRows({ length: 60 }),
allowedPageSizes: [5, 10, 15],
allowedPageSizes: [5, 10, 15, 0],
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export class PageSizeSelectorDemo extends React.PureComponent {
{ name: 'car', title: 'Car' },
],
rows: generateRows({ length: 60 }),
allowedPageSizes: [5, 10, 15],
allowedPageSizes: [5, 10, 15, 0],
};
}

Expand Down
22 changes: 17 additions & 5 deletions packages/dx-react-grid-bootstrap3/src/plugins/paging-panel.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import React from 'react';
import PropTypes from 'prop-types';
import { PagingPanel as PagingPanelBase } from '@devexpress/dx-react-grid';
import { Pager } from '../templates/pager';

const pagerTemplate = props => <Pager {...props} />;

export const PagingPanel = props => (
export const PagingPanel = ({ showAllText, ...restProps }) => (
<PagingPanelBase
pagerTemplate={pagerTemplate}
{...props}
pagerTemplate={
props => (
<Pager
showAllText={showAllText}
{...props}
/>
)
}
{...restProps}
/>
);

PagingPanel.propTypes = {
showAllText: PropTypes.string,
};
PagingPanel.defaultProps = {
showAllText: undefined,
};
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import React from 'react';
import PropTypes from 'prop-types';

export const PageSizeSelector = ({ pageSize, onPageSizeChange, allowedPageSizes }) => (
export const PageSizeSelector = ({
pageSize,
onPageSizeChange,
allowedPageSizes,
showAllText,
}) => (
<div style={{ display: 'inline-block' }}>
<select
className="form-control visible-xs-inline-block"
style={{ width: 'auto' }}
value={pageSize}
onChange={e => onPageSizeChange(parseInt(e.target.value, 10))}
>
{allowedPageSizes.map(val => <option key={val} value={val}>{val}</option>)}
{
allowedPageSizes.map(val =>
<option key={val} value={val}>
{val || showAllText}
</option>)
}
</select>
<ul
className="pagination hidden-xs"
Expand All @@ -28,7 +38,7 @@ export const PageSizeSelector = ({ pageSize, onPageSizeChange, allowedPageSizes
onPageSizeChange(item);
}}
>
{item}
{item || showAllText}
</a>
{/* eslint-enable jsx-a11y/href-no-hash */}
</li>
Expand All @@ -41,4 +51,10 @@ PageSizeSelector.propTypes = {
pageSize: PropTypes.number.isRequired,
onPageSizeChange: PropTypes.func.isRequired,
allowedPageSizes: PropTypes.arrayOf(PropTypes.number).isRequired,
showAllText: PropTypes.string,
};

PageSizeSelector.defaultProps = {
showAllText: 'All',
};

Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import React from 'react';
import { mount } from 'enzyme';
import { PageSizeSelector } from './page-size-selector';

describe('PageSizeSelector', () => {
describe('#render', () => {
const mountPageSizeSelector = ({
pageSize,
allowedPageSizes,
showAllText,
onPageSizeChange = () => {},
}) => mount(<PageSizeSelector
pageSize={pageSize}
allowedPageSizes={allowedPageSizes}
showAllText={showAllText}
onPageSizeChange={onPageSizeChange}
/>);

it('can show info about page sizes', () => {
const tree = mountPageSizeSelector({
pageSize: 10,
allowedPageSizes: [5, 10],
});

const mobileSelector = tree.find('select');
const desktopSelector = tree.find('ul.pagination');
const mobileSelectorItems = mobileSelector.find('option');
const desktopSelectorItems = desktopSelector.find('li');

expect(mobileSelector).toHaveLength(1);
expect(mobileSelector.prop('value')).toBe(10);
expect(mobileSelectorItems).toHaveLength(2);
expect(mobileSelectorItems.at(0).prop('value')).toBe(5);
expect(mobileSelectorItems.at(0).text()).toBe('5');
expect(mobileSelectorItems.at(1).prop('value')).toBe(10);
expect(mobileSelectorItems.at(1).text()).toBe('10');

expect(desktopSelector).toHaveLength(1);
expect(desktopSelectorItems).toHaveLength(2);
expect(desktopSelectorItems.at(0).prop('className')).toBe('');
expect(desktopSelectorItems.at(0).text()).toBe('5');
expect(desktopSelectorItems.at(1).prop('className')).toBe('active');
expect(desktopSelectorItems.at(1).text()).toBe('10');
});

it('can render the \'All\' item', () => {
const tree = mountPageSizeSelector({
pageSize: 10,
allowedPageSizes: [5, 10, 0],
});

const mobileSelector = tree.find('select');
const desktopSelector = tree.find('ul.pagination');
const mobileSelectorItems = mobileSelector.find('option');
const desktopSelectorItems = desktopSelector.find('li');

expect(mobileSelectorItems).toHaveLength(3);
expect(mobileSelectorItems.at(2).prop('value')).toBe(0);
expect(mobileSelectorItems.at(2).text()).toBe('All');

expect(desktopSelectorItems).toHaveLength(3);
expect(desktopSelectorItems.at(2).text()).toBe('All');
});

it('can customize the \'All\' item text', () => {
const tree = mountPageSizeSelector({
pageSize: 10,
allowedPageSizes: [5, 10, 0],
showAllText: 'Show all',
});

const mobileSelector = tree.find('select');
const desktopSelector = tree.find('ul.pagination');

expect(mobileSelector.find('option').at(2).text()).toBe('Show all');
expect(desktopSelector.find('li').at(2).text()).toBe('Show all');
});

it('can handle the \'onPageSizeChange\' event', () => {
const onPageSizeChange = jest.fn();
const tree = mountPageSizeSelector({
pageSize: 5,
allowedPageSizes: [5, 10],
onPageSizeChange,
});

const mobileSelector = tree.find('select');
const desktopSelector = tree.find('ul.pagination');

mobileSelector.simulate('change', { target: { value: 10 } });
desktopSelector.find('li > a').at(0).simulate('click');

expect(onPageSizeChange.mock.calls[0][0]).toBe(10);
expect(onPageSizeChange.mock.calls[1][0]).toBe(5);
});
});
});
7 changes: 7 additions & 0 deletions packages/dx-react-grid-bootstrap3/src/templates/pager.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ export const Pager = ({
onPageSizeChange,
allowedPageSizes,
totalCount,
showAllText,
}) => (
<div className="clearfix">
{!!allowedPageSizes.length && <PageSizeSelector
pageSize={pageSize}
onPageSizeChange={onPageSizeChange}
allowedPageSizes={allowedPageSizes}
showAllText={showAllText}
/>}
<Pagination
style={{
Expand Down Expand Up @@ -67,4 +69,9 @@ Pager.propTypes = {
onPageSizeChange: PropTypes.func.isRequired,
allowedPageSizes: PropTypes.arrayOf(PropTypes.number).isRequired,
totalCount: PropTypes.number.isRequired,
showAllText: PropTypes.string,
};

Pager.defaultProps = {
showAllText: undefined,
};
27 changes: 27 additions & 0 deletions packages/dx-react-grid-bootstrap3/src/templates/pager.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ describe('Pager', () => {
totalPages,
pageSize,
totalCount,
showAllText,
allowedPageSizes = [],
onPageSizeChange = () => {},
onCurrentPageChange = () => {},
Expand All @@ -17,6 +18,7 @@ describe('Pager', () => {
currentPage={currentPage}
totalCount={totalCount}
pageSize={pageSize}
showAllText={showAllText}
allowedPageSizes={allowedPageSizes}
onCurrentPageChange={onCurrentPageChange}
onPageSizeChange={onPageSizeChange}
Expand Down Expand Up @@ -96,5 +98,30 @@ describe('Pager', () => {
expect(next.hasClass('disabled')).toBeTruthy();
expect(onCurrentPageChange.mock.calls).toHaveLength(1);
});

it('renders page selector', () => {
const pageSizeSelector = mountPager({
totalPages: 10,
currentPage: 9,
totalCount: 96,
pageSize: 5,
allowedPageSizes: [5, 10],
showAllText: 'Show all',
}).find('PageSizeSelector');

expect(pageSizeSelector).toHaveLength(1);
expect(pageSizeSelector.at(0).prop('showAllText')).toBe('Show all');
});

it('doesn\'t render page selector if the allowedPageSizes option is not defined ', () => {
const pageSizeSelector = mountPager({
totalPages: 10,
currentPage: 9,
totalCount: 96,
pageSize: 5,
}).find('PageSizeSelector');

expect(pageSizeSelector).toHaveLength(0);
});
});
});
Loading