diff --git a/spec/pivotal-ui-react/pagination/pagination_spec.js b/spec/pivotal-ui-react/pagination/pagination_spec.js index 0e6c185c6..13df34bd0 100644 --- a/spec/pivotal-ui-react/pagination/pagination_spec.js +++ b/spec/pivotal-ui-react/pagination/pagination_spec.js @@ -1,92 +1,70 @@ import '../spec_helper'; - import {Pagination} from '../../../src/react/pagination'; describe('Pagination', () => { - let subject; - const renderComponent = props => ReactDOM.render(, root); beforeEach(() => { - subject = renderComponent(); + renderComponent(); }); it('renders a pagination component', () => { expect('div.pagination').toExist(); }); - it('has the "btn-group" class', () => { - expect('div.pagination').toHaveClass('btn-group'); - }); - it('has the "group" role', () => { expect('div.pagination').toHaveAttr('role', 'group'); }); - it('renders 1 button when no items are specified',() => { - expect($('.pagination button').length).toBe(3); - expect('.pagination button:eq(0)').toHaveText('‹'); - expect('.pagination button:eq(1)').toHaveText('1'); - expect('.pagination button:eq(2)').toHaveText('›'); - }); - - it('renders all buttons with the btn class', () => { - expect('.pagination button:eq(0)').toHaveClass('btn'); - expect('.pagination button:eq(1)').toHaveClass('btn'); - expect('.pagination button:eq(2)').toHaveClass('btn'); + it('renders 1 .pui-btn when no items are specified', () => { + expect($('.pagination .pui-btn').length).toBe(3); + expect('.pagination .pui-btn:eq(0) .icon svg').toHaveClass('icon-chevron_left'); + expect('.pagination .pui-btn:eq(1)').toHaveText('1'); + expect('.pagination .pui-btn:eq(2) .icon svg').toHaveClass('icon-chevron_right'); }); - it('renders all buttons with the btn-default-alt class', () => { - expect('.pagination button:eq(0)').toHaveClass('btn-default-alt'); - expect('.pagination button:eq(1)').toHaveClass('btn-default-alt'); - expect('.pagination button:eq(2)').toHaveClass('btn-default-alt'); + it('renders all buttons with flat class', () => { + expect('.pagination .pui-btn:eq(0)').toHaveClass('pui-btn-default-flat'); + expect('.pagination .pui-btn:eq(1)').toHaveClass('pui-btn-brand-flat'); + expect('.pagination .pui-btn:eq(2)').toHaveClass('pui-btn-default-flat'); }); describe('props', () => { it('renders the number of buttons specified in items, plus next and prev buttons', () => { - subject = renderComponent({items: 5}); + renderComponent({items: 5}); - expect('.pagination button:eq(0)').toHaveText('‹'); - expect('.pagination button:eq(1)').toHaveText('1'); - expect('.pagination button:eq(5)').toHaveText('5'); - expect('.pagination button:eq(6)').toHaveText('›'); - expect($('.pagination button').length).toBe(7); + expect('.pagination .pui-btn:eq(0) .icon svg').toHaveClass('icon-chevron_left'); + expect('.pagination .pui-btn:eq(1)').toHaveText('1'); + expect('.pagination .pui-btn:eq(5)').toHaveText('5'); + expect('.pagination .pui-btn:eq(6) .icon svg').toHaveClass('icon-chevron_right'); + expect($('.pagination .pui-btn').length).toBe(7); }); - it('does not render next when next is false',() => { - subject = renderComponent({next: false}); + it('does not render next when next is false', () => { + renderComponent({next: false}); - expect($('.pagination button').length).toBe(2); - expect('.pagination button:eq(0)').toHaveText('‹'); - expect('.pagination button:eq(1)').toHaveText('1'); + expect($('.pagination .pui-btn').length).toBe(2); + expect('.pagination .pui-btn:eq(0) .icon svg').toHaveClass('icon-chevron_left'); + expect('.pagination .pui-btn:eq(1)').toHaveText('1'); }); - it('does not render prev when prev is false',() => { - subject = renderComponent({prev: false}); + it('does not render prev when prev is false', () => { + renderComponent({prev: false}); - expect($('.pagination button').length).toBe(2); - expect('.pagination button:eq(0)').toHaveText('1'); - expect('.pagination button:eq(1)').toHaveText('›'); + expect($('.pagination .pui-btn').length).toBe(2); + expect('.pagination .pui-btn:eq(0)').toHaveText('1'); + expect('.pagination .pui-btn:eq(1) .icon svg').toHaveClass('icon-chevron_right'); }); - it('renders an active button when activePage number is specified', () => { - subject = renderComponent({activePage: 1}); - expect('.pagination button:eq(1)').toHaveClass('btn-default'); - expect('.pagination button:eq(1)').not.toHaveClass('btn-default-alt'); + it('renders an active .pui-btn when activePage number is specified', () => { + renderComponent({activePage: 1}); + expect('.pagination .pui-btn:eq(1)').toHaveClass('pui-btn-brand-flat'); + expect('.pagination .pui-btn:eq(1)').toHaveClass('active'); }); - it('renders large buttons when large prop is true', () => { - subject = renderComponent({large: true}); - expect('div.pagination').toHaveClass('btn-group-large'); - expect('div.pagination').not.toHaveClass('btn-group-small'); - expect('div.pagination').toHaveClass('btn-group'); - }); - - it('renders small buttons when small prop is true', () => { - subject = renderComponent({small: true}); - expect('div.pagination').toHaveClass('btn-group-small'); - expect('div.pagination').not.toHaveClass('btn-group-large'); - expect('div.pagination').toHaveClass('btn-group'); + it('passes the className to the top element', () => { + renderComponent({className: 'my-class'}); + expect('div.pagination').toHaveClass('my-class'); }); describe('onSelect', () => { @@ -94,26 +72,220 @@ describe('Pagination', () => { beforeEach(() => { onSelect = jasmine.createSpy('onSelect'); - subject = renderComponent({onSelect, items: 5}); + renderComponent({onSelect, items: 5, activePage: 2}); }); - it('calls on button click', () => { - $('.pagination button:eq(4)').simulate('click'); + it('calls on .pui-btn click', () => { + $('.pagination .pui-btn:eq(4)').simulate('click'); expect(onSelect).toHaveBeenCalledWith(jasmine.any(Object), {eventKey: 4}); }); it('calls on prev click', () => { - $('.pagination button:eq(0)').simulate('click'); + $('.pagination .pui-btn:eq(0)').simulate('click'); expect(onSelect).toHaveBeenCalledWith(jasmine.any(Object), {eventKey: 'prev'}); }); it('calls on next click', () => { - $('.pagination button:eq(6)').simulate('click'); + $('.pagination .pui-btn:eq(6)').simulate('click'); expect(onSelect).toHaveBeenCalledWith(jasmine.any(Object), {eventKey: 'next'}); }); }); }); + + describe('when the first page is active', () => { + beforeEach(() => { + renderComponent({items: 3}); + }); + + it('disables the left chevron', () => { + expect('.pagination .pui-btn:eq(0)').toBeDisabled(); + }); + }); + + describe('when the last page is active', () => { + beforeEach(() => { + renderComponent({items: 3, activePage: 3}); + }); + + it('disables the left chevron', () => { + expect('.pagination .pui-btn:last').toBeDisabled(); + }); + }); + + describe('when there are more than 6 pages, and activePage = 1', () => { + beforeEach(() => { + renderComponent({items: 20, activePage: 1, prev: false, next: false}); + }); + + it('renders pages for 1, 2, 3, 4, 20', () => { + expect($('button').length).toBe(5); + expect('button:eq(0)').toHaveText('1'); + expect('button:eq(1)').toHaveText('2'); + expect('button:eq(2)').toHaveText('3'); + expect('button:eq(3)').toHaveText('4'); + expect('button:eq(4)').toHaveText('20'); + }); + + it('renders ellipses (…)', () => { + expect('button:eq(3) + span').toHaveText(String.fromCharCode(8230)); + }); + }); + + describe('when there are more than 6 pages, and activePage = 2', () => { + beforeEach(() => { + renderComponent({items: 20, activePage: 2, prev: false, next: false}); + }); + + it('renders pages for 1, 2, 3, 4, 20', () => { + expect($('button').length).toBe(5); + expect('button:eq(0)').toHaveText('1'); + expect('button:eq(1)').toHaveText('2'); + expect('button:eq(2)').toHaveText('3'); + expect('button:eq(3)').toHaveText('4'); + expect('button:eq(4)').toHaveText('20'); + }); + + it('renders ellipses (…)', () => { + expect('button:eq(3) + span').toHaveText(String.fromCharCode(8230)); + }); + }); + + describe('when there are more than 6 pages, and activePage = 3', () => { + beforeEach(() => { + renderComponent({items: 20, activePage: 3, prev: false, next: false}); + }); + + it('renders pages for 1, 2, 3, 4, 20', () => { + expect($('button').length).toBe(5); + expect('button:eq(0)').toHaveText('1'); + expect('button:eq(1)').toHaveText('2'); + expect('button:eq(2)').toHaveText('3'); + expect('button:eq(3)').toHaveText('4'); + expect('button:eq(4)').toHaveText('20'); + }); + + it('renders ellipses (…)', () => { + expect('button:eq(3) + span').toHaveText(String.fromCharCode(8230)); + }); + }); + + describe('when there are more than 6 pages, and activePage = 4', () => { + beforeEach(() => { + renderComponent({items: 20, activePage: 4, prev: false, next: false}); + }); + + it('renders pages for 1, 3, 4, 5, 20', () => { + expect($('button').length).toBe(5); + expect('button:eq(0)').toHaveText('1'); + expect('button:eq(1)').toHaveText('3'); + expect('button:eq(2)').toHaveText('4'); + expect('button:eq(3)').toHaveText('5'); + expect('button:eq(4)').toHaveText('20'); + }); + + it('renders ellipses (…)', () => { + expect('button:eq(0) + span').toHaveText(String.fromCharCode(8230)); + expect('button:eq(3) + span').toHaveText(String.fromCharCode(8230)); + }); + }); + + describe('when there are more than 6 pages, and a page in the middle is active', () => { + beforeEach(() => { + renderComponent({items: 20, activePage: 10, prev: false, next: false}); + }); + + it('renders pages for 1, 9, 10, 11, 20', () => { + expect($('button').length).toBe(5); + expect('button:eq(0)').toHaveText('1'); + expect('button:eq(1)').toHaveText('9'); + expect('button:eq(2)').toHaveText('10'); + expect('button:eq(3)').toHaveText('11'); + expect('button:eq(4)').toHaveText('20'); + }); + + it('renders ellipses (…)', () => { + expect('button:eq(0) + span').toHaveText(String.fromCharCode(8230)); + expect('button:eq(3) + span').toHaveText(String.fromCharCode(8230)); + }); + }); + + describe('when there are more than 6 pages, and activePage = 17', () => { + beforeEach(() => { + renderComponent({items: 20, activePage: 17, prev: false, next: false}); + }); + + it('renders pages for 1, 16, 17, 18, 20', () => { + expect($('button').length).toBe(5); + expect('button:eq(0)').toHaveText('1'); + expect('button:eq(1)').toHaveText('16'); + expect('button:eq(2)').toHaveText('17'); + expect('button:eq(3)').toHaveText('18'); + expect('button:eq(4)').toHaveText('20'); + }); + + it('renders ellipses (…)', () => { + expect('button:eq(0) + span').toHaveText(String.fromCharCode(8230)); + expect('button:eq(3) + span').toHaveText(String.fromCharCode(8230)); + }); + }); + + describe('when there are more than 6 pages, and activePage = 18', () => { + beforeEach(() => { + renderComponent({items: 20, activePage: 18, prev: false, next: false}); + }); + + it('renders pages for 1, 17, 18, 19, 20', () => { + expect($('button').length).toBe(5); + expect('button:eq(0)').toHaveText('1'); + expect('button:eq(1)').toHaveText('17'); + expect('button:eq(2)').toHaveText('18'); + expect('button:eq(3)').toHaveText('19'); + expect('button:eq(4)').toHaveText('20'); + }); + + it('renders ellipses (…)', () => { + expect('button:eq(0) + span').toHaveText(String.fromCharCode(8230)); + }); + }); + + describe('when there are more than 6 pages, and activePage = 19', () => { + beforeEach(() => { + renderComponent({items: 20, activePage: 19, prev: false, next: false}); + }); + + it('renders pages for 1, 17, 18, 19, 20', () => { + expect($('button').length).toBe(5); + expect('button:eq(0)').toHaveText('1'); + expect('button:eq(1)').toHaveText('17'); + expect('button:eq(2)').toHaveText('18'); + expect('button:eq(3)').toHaveText('19'); + expect('button:eq(4)').toHaveText('20'); + }); + + it('renders ellipses (…)', () => { + expect('button:eq(0) + span').toHaveText(String.fromCharCode(8230)); + }); + }); + + describe('when there are more than 6 pages, and activePage = 20', () => { + beforeEach(() => { + renderComponent({items: 20, activePage: 20, prev: false, next: false}); + }); + + it('renders pages for 1, 17, 18, 19, 20', () => { + expect($('button').length).toBe(5); + expect('button:eq(0)').toHaveText('1'); + expect('button:eq(1)').toHaveText('17'); + expect('button:eq(2)').toHaveText('18'); + expect('button:eq(3)').toHaveText('19'); + expect('button:eq(4)').toHaveText('20'); + }); + + it('renders ellipses (…)', () => { + expect('button:eq(0) + span').toHaveText(String.fromCharCode(8230)); + }); + }); }); diff --git a/src/css/pagination/pagination.scss b/src/css/pagination/pagination.scss index da030acf4..702259ff1 100644 --- a/src/css/pagination/pagination.scss +++ b/src/css/pagination/pagination.scss @@ -1,9 +1,16 @@ @import "../pui-variables"; .pagination { - font-size: 0; - list-style-type: none; - margin: 0; - padding: 0; - box-sizing: border-box; + .active { + color: $brand-8; + text-decoration: underline; + } + + .pui-btn { + padding: 0 $base-unit; + + &:hover { + text-decoration: underline; + } + } } diff --git a/src/react/pagination/pagination.js b/src/react/pagination/pagination.js index fd826d219..e50630c13 100644 --- a/src/react/pagination/pagination.js +++ b/src/react/pagination/pagination.js @@ -1,6 +1,8 @@ import React from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; +import {BrandButton, DefaultButton} from '../buttons'; +import {Icon} from '../iconography'; class PaginationButton extends React.PureComponent { static propTypes = { @@ -19,13 +21,15 @@ class PaginationButton extends React.PureComponent { onSelect && onSelect(e, {eventKey}); }; + render() { - const {content, active} = this.props; - return (); + ); } } @@ -35,9 +39,7 @@ export class Pagination extends React.PureComponent { next: PropTypes.bool, prev: PropTypes.bool, activePage: PropTypes.number, - onSelect: PropTypes.func, - small: PropTypes.bool, - large: PropTypes.bool + onSelect: PropTypes.func }; static defaultProps = { @@ -45,7 +47,8 @@ export class Pagination extends React.PureComponent { next: true, prev: true, onSelect: () => { - } + }, + activePage: 1 }; componentDidMount() { @@ -53,29 +56,101 @@ export class Pagination extends React.PureComponent { } render() { - const {items, next, prev, activePage, onSelect, small, large, ...props} = this.props; - const paginationButtons = []; - for (let i = 0; i < items; i++) { - const isActive = (i + 1 === activePage); - paginationButtons.push(); + const {items, next, prev, activePage, onSelect, className, ...props} = this.props; + + const elements = []; + + if (items < 6) { + for (let i = 0; i < items; i++) { + elements.push(); + } + } else { + if (activePage <= 3) { + for (let i = 0; i < 4; i++) { + elements.push(); + } + } else { + elements.push(); + elements.push(); + } + + if (activePage > 3 && activePage < items - 2) { + for (let i = activePage - 2; i < activePage + 1; i++) { + elements.push(); + } + } + + if (activePage >= items - 2) { + for (let i = items - 4; i < items; i++) { + elements.push(); + } + } else { + elements.push(); + elements.push(); + } } + const prevButton = (, + iconOnly: true + }}/>); - const prevButton = ; - const nextButton = ; + const nextButton = (, + iconOnly: true + }}/>); - return (
+ return (
{prev ? prevButton : null} - {paginationButtons} + {elements} {next ? nextButton : null}
); }