-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/stcom-827-pagination-component' …
…into hierarchy-tree-poc
- Loading branch information
Showing
11 changed files
with
391 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
@import '../variables.css'; | ||
|
||
/** | ||
* Default styling | ||
*/ | ||
|
||
.pagination { | ||
display: flex; | ||
list-style: none; | ||
padding: 4px 0; | ||
margin: 0; | ||
|
||
&.fillWidth { | ||
width: 100%; | ||
justify-content: space-between; | ||
} | ||
|
||
& .paginationItem { | ||
padding: 0 2px; | ||
margin: 0 1px; | ||
} | ||
} | ||
|
||
.paginationLink { | ||
composes: button from "../Button/Button.css"; | ||
padding: 0 var(--gutter-static-two-thirds, 8px); | ||
margin: 0; | ||
|
||
&[aria-disabled=true] { | ||
color: var(--color-text-p2); | ||
transition: opacity ease-in-out 500ms; | ||
pointer-events: none; | ||
} | ||
|
||
&.numberLink { | ||
min-width: 1.72rem; | ||
padding: 0 4px; | ||
margin: 0; | ||
} | ||
|
||
&:visited { | ||
color: inherit; | ||
} | ||
|
||
&::before { | ||
border-radius: 999px; | ||
} | ||
|
||
/** | ||
* Button Style: Default | ||
*/ | ||
|
||
&.default { | ||
background-color: transparent; | ||
border: 1px solid var(--primary); | ||
color: var(--primary); | ||
|
||
& :global .stripes__icon { | ||
fill: var(--primary); | ||
} | ||
} | ||
|
||
/** | ||
* Button style primary | ||
*/ | ||
|
||
&.primary { | ||
background-color: var(--primary); | ||
border: 1px solid var(--primary); | ||
color: #fff; | ||
|
||
&:hover { | ||
opacity: 0.9; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import ReactPaginate from 'react-paginate'; | ||
import { FormattedMessage } from 'react-intl'; | ||
import Icon from '../Icon'; | ||
import css from './Pagination.css'; | ||
|
||
// < # ... # # # ... # > | ||
// |_| |___| |_____| |___| |_| | ||
// OUTER BREAK CENTER BREAK OUTER | ||
|
||
const MINIMAL_DISPLAY_THRESHOLD = 6; | ||
const MINIMAL_DISPLAY_COUNT = 3; | ||
const EXTENDED_DISPLAY_COUNT = 6; | ||
const MAX_EXTENDED_OUTER_PAGES = 1; | ||
|
||
const propTypes = { | ||
/** Class to be applied to `<nav>` containter */ | ||
className: PropTypes.string, | ||
/** Allows you to control the pagination and define the current page. */ | ||
currentPage: PropTypes.number, | ||
/** fills width of container, placing previous/next buttons at either end */ | ||
fillWidth: PropTypes.bool, | ||
/** The method called to generate the href attribute value for each page. */ | ||
hrefBuilder: PropTypes.func.isRequired, | ||
id: PropTypes.string, | ||
/** Set accessible label for `nav` container */ | ||
label: PropTypes.string, | ||
/** The method to call when a page is clicked. Exposes the current page object as an argument. */ | ||
onPageChange: PropTypes.func, | ||
/** Total number of pages. */ | ||
pageCount: PropTypes.number, | ||
/** Whether or not to show the previous and next labels */ | ||
showLabels: PropTypes.bool, | ||
}; | ||
|
||
const Pagination = ({ | ||
id, | ||
pageCount, | ||
onPageChange, | ||
hrefBuilder, | ||
fillWidth, | ||
currentPage, | ||
label = 'pagination', | ||
showLabels = true, | ||
...props | ||
}) => { | ||
return ( | ||
<nav id={id} data-testid="pagination-component" aria-label={label} data-test-pagination> | ||
<ReactPaginate | ||
pageCount={pageCount} | ||
pageRangeDisplayed={pageCount < MINIMAL_DISPLAY_THRESHOLD ? EXTENDED_DISPLAY_COUNT : MINIMAL_DISPLAY_COUNT} | ||
marginPagesDisplayed={pageCount < MINIMAL_DISPLAY_THRESHOLD ? 0 : MAX_EXTENDED_OUTER_PAGES} | ||
nextLabel={( | ||
<div data-test-pagination-next> | ||
<Icon size="small" icon="caret-right" iconPosition="end"> | ||
<FormattedMessage id="stripes-components.next"> | ||
{ (text) => <span className={`${showLabels ? '' : 'sr-only'}`}>{text}</span>} | ||
</FormattedMessage> | ||
</Icon> | ||
</div> | ||
)} | ||
previousLabel={( | ||
<div data-test-pagination-previous> | ||
<Icon size="small" icon="caret-left"> | ||
<FormattedMessage id="stripes-components.previous"> | ||
{ (text) => <span className={`${showLabels ? '' : 'sr-only'}`}>{text}</span>} | ||
</FormattedMessage> | ||
</Icon> | ||
</div> | ||
)} | ||
breakLabel={<Icon icon="ellipsis" aria-label="ellipsis" />} | ||
onPageChange={onPageChange} | ||
pageClassName={css.paginationItem} | ||
containerClassName={`${css.pagination} ${fillWidth ? css.fillWidth : ''}`} | ||
pageLinkClassName={`${css.paginationLink} ${css.numberLink}`} | ||
activeLinkClassName={css.primary} | ||
nextLinkClassName={css.paginationLink} | ||
previousLinkClassName={css.paginationLink} | ||
breakLinkClassName={css.paginationLink} | ||
forcePage={currentPage} | ||
hrefBuilder={hrefBuilder} | ||
{...props} | ||
/> | ||
</nav> | ||
); | ||
}; | ||
|
||
Pagination.propTypes = propTypes; | ||
|
||
export default Pagination; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default } from './Pagination'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
|
||
# Pagination | ||
The Pagination component is used for nagivation between pages of a list of results. | ||
|
||
We use [react-paginate](https://github.com/AdeleD/react-paginate) under the hood, more detail can be found at their we expose their API along with a few useful props. | ||
|
||
## Basic Usage | ||
|
||
``` | ||
// take actions within your app corresponding to the selected page... | ||
const handlePageClick = (page) => { | ||
// page is an object with {selected} property - which gives the currently selected page index. | ||
// console.log(page); | ||
} | ||
// generate the hrefs for links if necessary (return the full string) | ||
const resultsHrefBuilder = (page) => { | ||
// return '#'; | ||
} | ||
<Pagination | ||
pageCount={20} | ||
onPageChange={handlePageClick} | ||
hrefBuilder={resultsHrefBuilder} | ||
/> | ||
``` | ||
|
||
## Display modes based on page count... | ||
For less than 6 pages, 3 page links will be visible between previous and next buttons (carets.) | ||
``` | ||
< 1 2 3 > | ||
``` | ||
|
||
For more than 6 pages, extended mode is activated - it includes collected central links between ellipsis and outer "margin" links. | ||
``` | ||
< 1 ... 4 5 6 ... 10 > | ||
``` | ||
|
||
## Props | ||
|
||
Name | type | description | default | required | ||
--- | --- | --- | --- | --- | ||
`id` | string | Applies the 'id' attribute to the outer `<nav>` element | --- | --- | ||
`pageCount`| number | Used to set starting/ending page numbers (1 - pageCount) | --- | required | ||
`onPageChange` | func | --- | function called when page button is clicked | --- | ||
`hrefBuilder` | func | function for generating hrefs for individual buttons. Provides the page object | --- | ||
`fillWidth` | bool | if `true`, pagination will fill its parent container, placing previous and next buttons at either end, with page buttons distributed evenly between | false | --- | ||
`currentPage` | number | manually sets the current page | --- | --- | ||
`label` | string | label for outer `<nav>` element | --- | 'pagination' | --- | ||
`showLabels` | bool | whether or not to display the visible "previous" and "next" labels. | true | --- | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/** | ||
* Pagination Component: Basic Usage | ||
*/ | ||
|
||
import React from 'react'; | ||
import { action } from '@storybook/addon-actions'; | ||
import Pagination from '..'; | ||
|
||
// eslint-disable-next-line | ||
const handlePageClick = (page) => { | ||
action(page); | ||
}; | ||
|
||
export default () => ( | ||
<div> | ||
<h3>20 pages</h3> | ||
<Pagination | ||
pageCount={20} | ||
onPageChange={handlePageClick} | ||
hrefBuilder={() => '#'} | ||
/> | ||
<h3>3 or fewer pages, hidden previous and next labels</h3> | ||
<Pagination | ||
pageCount={3} | ||
onPageChange={handlePageClick} | ||
hrefBuilder={() => '#'} | ||
showLabels={false} | ||
/> | ||
<h3>Fill width of container</h3> | ||
<div style={{ width: '600px', border: '1px solid #ccc' }}> | ||
<Pagination | ||
pageCount={3} | ||
onPageChange={handlePageClick} | ||
hrefBuilder={() => '#'} | ||
fillWidth | ||
/> | ||
</div> | ||
</div> | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import React from 'react'; | ||
import { storiesOf } from '@storybook/react'; | ||
import withReadme from 'storybook-readme/with-readme'; | ||
import readme from '../readme.md'; | ||
import BasicUsage from './BasicUsage'; | ||
|
||
storiesOf('Pagination', module) | ||
.addDecorator(withReadme(readme)) | ||
.add('Basic Usage', () => <BasicUsage />); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import React from 'react'; | ||
import { expect } from 'chai'; | ||
import sinon from 'sinon'; | ||
import { | ||
beforeEach, | ||
describe, | ||
it, | ||
} from '@bigtest/mocha'; | ||
|
||
import Pagination from '../Pagination'; | ||
|
||
import { mountWithContext } from '../../../tests/helpers'; | ||
import PaginationInteractor from './interactor'; | ||
|
||
const clickHandler = sinon.spy(); | ||
|
||
describe('Pagination', () => { | ||
const pagination = new PaginationInteractor(); | ||
|
||
describe('rendering', () => { | ||
beforeEach(async () => { | ||
await mountWithContext( | ||
<Pagination | ||
pageCount={20} | ||
onPageChange={clickHandler} | ||
hrefBuilder={() => '#'} | ||
/> | ||
); | ||
}); | ||
|
||
it('should render pagination links', () => { | ||
expect(pagination.isPresent).to.be.true; | ||
}); | ||
|
||
it('last number link should display 20', () => { | ||
expect(pagination.lastNumber.number).to.equal('20'); | ||
}); | ||
|
||
it('previous/next buttons display labels', () => { | ||
expect(pagination.nextlink.labelHidden).to.be.false; | ||
expect(pagination.nextlink.labelHidden).to.be.false; | ||
}); | ||
|
||
describe('clicking the next button', () => { | ||
beforeEach(async () => { | ||
clickHandler.resetHistory; | ||
await pagination.nextlink.click(); | ||
}); | ||
|
||
it('calls the pageChange handler', () => { | ||
expect(clickHandler.calledOnce).to.be.true; | ||
}); | ||
}); | ||
describe('clicking the previous button', () => { | ||
beforeEach(async () => { | ||
clickHandler.resetHistory; | ||
await pagination.previouslink.click(); | ||
}); | ||
|
||
it('calls the pageChange handler', () => { | ||
expect(clickHandler.calledOnce).to.be.true; | ||
}); | ||
}); | ||
}); | ||
|
||
describe('hidden previous/next labels', () => { | ||
beforeEach(async () => { | ||
await mountWithContext( | ||
<Pagination | ||
pageCount={3} | ||
hrefBuilder={() => '#'} | ||
showLabels={false} | ||
/> | ||
); | ||
}); | ||
|
||
it('previous/next buttons hide labels', () => { | ||
expect(pagination.nextlink.labelHidden).to.be.true; | ||
expect(pagination.nextlink.labelHidden).to.be.true; | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.