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 7 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
31 changes: 25 additions & 6 deletions packages/dx-grid-core/src/plugins/paging-state/computeds.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
export const paginate = (rows, pageSize, page) =>
rows.slice(pageSize * page, pageSize * (page + 1));
const ALL_PAGES_TITLE = 'All';
Copy link
Contributor

Choose a reason for hiding this comment

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

This const seems unused

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Used here export const pageSizeTitle = pageSize => pageSize || ALL_PAGES_TITLE;

Copy link
Contributor

Choose a reason for hiding this comment

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

The pageSizeTitle export is strange, isn't it? It has a localizable string that can't be configured by a user.


export const paginate = (rows, pageSize, page) => (
pageSize ?
rows.slice(pageSize * page, pageSize * (page + 1)) :
rows
);

export const ensurePageHeaders = (rows, pageSize) => {
const result = rows.slice();
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 result;
}

const headers = [];
let currentIndex = 0;
Expand All @@ -27,13 +35,24 @@ 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;
};

export const pageSizeTitle = pageSize => pageSize || ALL_PAGES_TITLE;
45 changes: 42 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 @@ -5,6 +5,7 @@ import {
totalCount,
firstRowOnPage,
lastRowOnPage,
pageSizeTitle,
} from './computeds';

describe('PagingState computeds', () => {
Expand All @@ -20,6 +21,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 +145,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,20 +182,36 @@ 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', () => {
const count = lastRowOnPage(1, 5, 9);
expect(count).toEqual(9);
});
});

describe('#pageSizeTitle', () => {
it('should work', () => {
let title = pageSizeTitle(10);
expect(title).toEqual(10);

title = pageSizeTitle(0);
expect(title).toEqual('All');
});
});
});
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { pageSizeTitle } from '@devexpress/dx-grid-core';

export const PageSizeSelector = ({ pageSize, onPageSizeChange, allowedPageSizes }) => (
<div style={{ display: 'inline-block' }}>
Expand All @@ -9,7 +10,7 @@ export const PageSizeSelector = ({ pageSize, onPageSizeChange, allowedPageSizes
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}>{pageSizeTitle(val)}</option>)}
</select>
<ul
className="pagination hidden-xs"
Expand All @@ -28,7 +29,7 @@ export const PageSizeSelector = ({ pageSize, onPageSizeChange, allowedPageSizes
onPageSizeChange(item);
}}
>
{item}
{pageSizeTitle(item)}
</a>
{/* eslint-enable jsx-a11y/href-no-hash */}
</li>
Expand Down
26 changes: 16 additions & 10 deletions packages/dx-react-grid-material-ui/src/templates/drop-down-menu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,13 @@ class DropDownMenuBase extends React.PureComponent {

constructor(props) {
super(props);

const selectedItem = this.props.selectedItem;
const { selectedItem, items, defaultTitle } = this.props;

this.state = {
anchorEl: undefined,
open: false,
selectedIndex: this.props.items.findIndex(item => item === selectedItem),
title: this.props.defaultTitle || selectedItem,
selectedIndex: items.findIndex(item => item === selectedItem),
title: defaultTitle || selectedItem,
};

this.handleClick = this.handleClick.bind(this);
Expand All @@ -55,12 +54,17 @@ class DropDownMenuBase extends React.PureComponent {
}

handleMenuItemClick(event, index) {
let title = this.props.items[index];
if (index === 0 && this.props.defaultTitle) {
title = this.props.defaultTitle;
const { items, defaultTitle, itemTemplate, onItemClick } = this.props;
let title = items[index];

if (index === 0 && defaultTitle) {
title = defaultTitle;
} else if (itemTemplate) {
title = itemTemplate(title);
}

this.props.onItemClick(title, index);
onItemClick(items[index], index);

this.setState({
selectedIndex: index,
open: false,
Expand All @@ -73,7 +77,7 @@ class DropDownMenuBase extends React.PureComponent {
}

render() {
const { items, classes, className } = this.props;
const { items, classes, className, itemTemplate } = this.props;
const { anchorEl, open, selectedIndex, title } = this.state;
const titleClasses = classNames({
[classes.title]: true,
Expand Down Expand Up @@ -106,7 +110,7 @@ class DropDownMenuBase extends React.PureComponent {
selected={index === selectedIndex}
onClick={event => this.handleMenuItemClick(event, index)}
>
{item}
{itemTemplate ? itemTemplate(item) : item}
</MenuItem>
))}
</Menu>
Expand All @@ -130,13 +134,15 @@ DropDownMenuBase.propTypes = {
])).isRequired,
classes: PropTypes.object.isRequired,
onItemClick: PropTypes.func.isRequired,
itemTemplate: PropTypes.func,
className: PropTypes.string,
};

DropDownMenuBase.defaultProps = {
className: null,
selectedItem: undefined,
defaultTitle: undefined,
itemTemplate: undefined,
};

export const DropDownMenu = withStyles(styleSheet)(DropDownMenuBase);
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles, createStyleSheet } from 'material-ui/styles';
import { pageSizeTitle } from '@devexpress/dx-grid-core';
import { DropDownMenu } from './drop-down-menu';

const styleSheet = createStyleSheet('PageSizeSelector', theme => ({
Expand All @@ -23,7 +24,7 @@ const styleSheet = createStyleSheet('PageSizeSelector', theme => ({
},
pageSizeSelector: {
paddingRight: theme.spacing.unit * 2,
marginTop: 4,
marginTop: theme.spacing.unit,
},
},
}));
Expand All @@ -36,6 +37,7 @@ const PageSizeSelectorBase = ({ pageSize, onPageSizeChange, allowedPageSizes, cl
<DropDownMenu
selectedItem={pageSize}
items={allowedPageSizes}
itemTemplate={pageSizeTitle}
onItemClick={(item) => {
onPageSizeChange(item);
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const paginationStyleSheet = createStyleSheet('Pagination', theme => ({
rowsLabel: {
...theme.typography.caption,
paddingRight: theme.spacing.unit * 5,
lineHeight: `${theme.spacing.unit * 5}px`,
},
button: {
minWidth: theme.spacing.unit * 2,
Expand Down
2 changes: 1 addition & 1 deletion packages/dx-react-grid/docs/guides/paging.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ In the following example, we use the uncontrolled mode and specify only the init

## Page Size Selection

To enable page size selection, specify the `allowedPageSizes` property of the `PagingPanel` plugin.
To enable page size selection, specify the `allowedPageSizes` property of the `PagingPanel` plugin. To show all rows on a page you can use `0` as one of the `allowedPageSizes` values.

The example below demonstrates the basic configuration for the uncontrolled mode. The `defaultPageSize` property of the `PagingState` plugin is used to define the initial page size.

Expand Down
2 changes: 1 addition & 1 deletion packages/dx-react-grid/docs/reference/paging-state.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ totalCount | number | | Specifies the total rows count
currentPage | number | | Specifies the current page number
defaultCurrentPage | number | 0 | Specifies the initial current page for the uncontrolled mode
onCurrentPageChange | (currentPage: number) => void | | Handles current page changes
pageSize | number | | Specifies the page size
pageSize | number &#124; | | Specifies the page size
Copy link
Contributor

Choose a reason for hiding this comment

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

Also, remove redundant | symbol

defaultPageSize | number | 10 | Specifies the initial page size for the uncontrolled mode
onPageSizeChange | (pageSize: number) => void | | Handles page size changes

Expand Down
2 changes: 1 addition & 1 deletion packages/dx-react-grid/src/plugins/paging-panel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const PagingPanel = ({ pagerTemplate, allowedPageSizes }) => (
);

PagingPanel.propTypes = {
allowedPageSizes: PropTypes.arrayOf(PropTypes.number),
allowedPageSizes: PropTypes.arrayOf(PropTypes.number).isRequired,
pagerTemplate: PropTypes.func.isRequired,
};

Expand Down
5 changes: 4 additions & 1 deletion packages/dx-react-grid/src/plugins/paging-state.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ export class PagingState extends React.PureComponent {

<Getter name="currentPage" value={currentPage} />
<Getter name="pageSize" value={pageSize} />
<Getter name="totalPages" value={Math.max(1, Math.ceil(totalCount / pageSize))} />
<Getter
name="totalPages"
value={pageSize ? Math.max(1, Math.ceil(totalCount / pageSize)) : 1}
/>
<Getter name="totalCount" value={totalCount || 0} />
</PluginContainer>
);
Expand Down