diff --git a/src/components/PaginationRow/PaginationRow.js b/src/components/PaginationRow/PaginationRow.js new file mode 100644 index 00000000000..a7a9327f720 --- /dev/null +++ b/src/components/PaginationRow/PaginationRow.js @@ -0,0 +1,223 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ClassNames from 'classnames'; + +import { MenuItem, DropdownButton, Icon } from '../../index'; + +const ArrowIcon = props => { + const name = `angle-${props.name}`; + return ; +}; + +ArrowIcon.propTypes = { + name: PropTypes.oneOf(['left', 'double-left', 'right', 'double-right']) +}; + +class PaginationRow extends React.Component { + constructor(props) { + super(props); + + this.initPagination(props); + this.state = { + pageChangeValue: Number(props.currentPage) + }; + } + + componentWillReceiveProps(nextProps) { + if (this.props.currentPage !== nextProps.currentPage) { + this.setState({ pageChangeValue: Number(nextProps.currentPage) }); + } + + this.initPagination(nextProps); + } + + initPagination(props) { + this.perPage = Number(props.perPage); + this.totalCount = Number(props.totalCount); + this.currentPage = Number(props.currentPage); + } + + msg(key) { + return this.props.messages[key] || PaginationRow.defaultMessages[key]; + } + + totalPages() { + return Math.ceil(this.props.totalCount / this.perPage); + } + + setPageRelative(diff) { + this.setPage(Number(this.props.currentPage) + diff); + } + + setPage(page) { + if (page !== '') { + this.props.onPageSet(Number(page)); + } else { + console.error("Page can't be blank"); + } + } + + handlePageChange(e) { + this.setState({ pageChangeValue: e.target.value }); + } + + handleFormSubmit(e) { + this.setPage(this.state.pageChangeValue); + e.preventDefault(); + } + + renderPerPageDropdown() { + const { perPageOptions, onPerPageSet } = this.props; + + return ( + + {perPageOptions.map(opt => { + return ( + onPerPageSet(opt)} + key={opt} + > + {opt} + + ); + })} + + ); + } + + render() { + const perPageDropdown = this.renderPerPageDropdown(); + + const displayedRangeStart = (this.currentPage - 1) * this.perPage + 1; + const displayedRangeEnd = Math.min( + displayedRangeStart + this.perPage - 1, + this.totalCount + ); + const displayedRange = `${displayedRangeStart}-${displayedRangeEnd}`; + + const backButtonsClass = this.currentPage === 1 ? 'disabled' : ''; + const nextButtonsClass = + this.currentPage * this.perPage >= this.totalCount ? 'disabled' : ''; + + const totalPages = this.totalPages(); + + const classes = ClassNames(this.props.className, 'clearfix'); + + return ( +
+
this.handleFormSubmit(e)}> +
+
{perPageDropdown}
+   + {this.msg('perPage')} +
+
+ + + {displayedRange} + +  {this.msg('of')}  + + {this.totalCount} + + + + + + + this.handlePageChange(e)} + type="text" + /> + + {this.msg('of')}  + {totalPages} + + + +
+
+
+ ); + } +} + +PaginationRow.propTypes = { + /** Options for the per page dropdown */ + perPageOptions: PropTypes.array, + /** Current per page setting */ + perPage: PropTypes.number.isRequired, // eslint-disable-line react/no-unused-prop-types + /** Total number of items to paginate */ + totalCount: PropTypes.number.isRequired, + /** Index of page that is currently shown, starting from 1 */ + currentPage: PropTypes.number.isRequired, + /** A callback triggered when the per page dropdown value is selected */ + onPerPageSet: PropTypes.func, + /** A callback triggered when a page is switched */ + onPageSet: PropTypes.func, + /** Strings in the component, see PaginationRow.defaultMessages for details */ + messages: PropTypes.object, + /** Class name for the form element */ + className: PropTypes.string +}; + +PaginationRow.defaultProps = { + perPageOptions: [], + onPageSet: p => {}, + onPerPageSet: pp => {}, + messages: {}, + className: 'content-view-pf-pagination' +}; + +PaginationRow.defaultMessages = { + firstPage: 'First Page', + previousPage: 'Previous Page', + nextPage: 'Next Page', + lastPage: 'Last Page', + perPage: 'per page', + of: 'of' +}; + +export default PaginationRow; diff --git a/src/components/PaginationRow/PaginationRow.stories.js b/src/components/PaginationRow/PaginationRow.stories.js new file mode 100644 index 00000000000..a445bf0e852 --- /dev/null +++ b/src/components/PaginationRow/PaginationRow.stories.js @@ -0,0 +1,51 @@ +import React from 'react'; +import { storiesOf } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; +import { withKnobs, select, text } from '@storybook/addon-knobs'; +import { defaultTemplate } from '../../../storybook/decorators/storyTemplates'; +import { PaginationRow } from './index'; + +const stories = storiesOf('PaginationRow', module); + +stories.addDecorator( + defaultTemplate({ + title: 'Pagination Row', + documentationLink: + 'http://www.patternfly.org/pattern-library/navigation/pagination/' + }) +); +stories.addDecorator(withKnobs); +stories.addWithInfo('Basic example', '', () => { + const page = select('Page', [1, 3, 8], 1); + const totalCount = select('Total items', [75, 80, 81], 75); + + return ( + + ); +}); + +stories.addWithInfo('With translations', '', () => { + var messages = {}; + for (var key in PaginationRow.defaultMessages) { + messages[key] = text(key, PaginationRow.defaultMessages[key]); + } + + return ( + + ); +}); diff --git a/src/components/PaginationRow/PaginationRow.test.js b/src/components/PaginationRow/PaginationRow.test.js new file mode 100644 index 00000000000..799a00e1304 --- /dev/null +++ b/src/components/PaginationRow/PaginationRow.test.js @@ -0,0 +1,48 @@ +/* eslint-env jest */ + +import React from 'react'; +import renderer from 'react-test-renderer'; + +import PaginationRow from './PaginationRow'; + +test('PaginationRow renders properly the first page', () => { + const component = renderer.create( + + ); + + let tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); + +test('PaginationRow renders properly a middle page', () => { + const component = renderer.create( + + ); + + let tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); + +test('PaginationRow renders properly the last page', () => { + const component = renderer.create( + + ); + + let tree = component.toJSON(); + expect(tree).toMatchSnapshot(); +}); diff --git a/src/components/PaginationRow/__snapshots__/PaginationRow.test.js.snap b/src/components/PaginationRow/__snapshots__/PaginationRow.test.js.snap new file mode 100644 index 00000000000..49b491948f9 --- /dev/null +++ b/src/components/PaginationRow/__snapshots__/PaginationRow.test.js.snap @@ -0,0 +1,574 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PaginationRow renders properly a middle page 1`] = ` +
+
+
+
+
+ + +
+
+   + + per page + +
+
+ + + 31-40 + +   + of +   + + 75 + + + + + + + of +   + + 8 + + + +
+
+
+`; + +exports[`PaginationRow renders properly the first page 1`] = ` +
+
+
+
+
+ + +
+
+   + + per page + +
+
+ + + 1-10 + +   + of +   + + 75 + + + + + + + of +   + + 8 + + + +
+
+
+`; + +exports[`PaginationRow renders properly the last page 1`] = ` +
+
+
+
+
+ + +
+
+   + + per page + +
+
+ + + 71-75 + +   + of +   + + 75 + + + + + + + of +   + + 8 + + + +
+
+
+`; diff --git a/src/components/PaginationRow/index.js b/src/components/PaginationRow/index.js new file mode 100644 index 00000000000..4ff16c8013a --- /dev/null +++ b/src/components/PaginationRow/index.js @@ -0,0 +1 @@ +export { default as PaginationRow } from './PaginationRow';