Skip to content

Commit

Permalink
Merge pull request #262 from dhis2/tech215-pagination
Browse files Browse the repository at this point in the history
 feat: introduce pagination component and unit tests
  • Loading branch information
HendrikThePendric authored Sep 17, 2020
2 parents 6e5df0b + d309081 commit 3d46b78
Show file tree
Hide file tree
Showing 16 changed files with 778 additions and 2 deletions.
17 changes: 17 additions & 0 deletions packages/widgets/i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,20 @@ msgstr ""

msgid "Error: {{ ERRORMESSAGE }}"
msgstr ""

msgid "Next"
msgstr ""

msgid "Page"
msgstr ""

msgid "Items per page"
msgstr ""

msgid ""
"Page {{page}} of {{pageCount}}, items {{firstItem}}-{{lastItem}} of "
"{{total}}"
msgstr ""

msgid "Previous"
msgstr ""
68 changes: 68 additions & 0 deletions packages/widgets/src/Pagination/PageControls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react'
import propTypes from '@dhis2/prop-types'
import { Button } from '@dhis2/ui-core'
import { ChevronRight, ChevronLeft } from '@dhis2/ui-icons'
import { spacers } from '@dhis2/ui-constants'

import translate from '../translate'

const PageControls = ({
dataTest,
onClick,
nextPageText,
page,
pageCount,
previousPageText,
}) => (
<div data-test={`${dataTest}-pagecontrols`}>
<Button
secondary
className="button-previous"
small
disabled={page === 1}
onClick={() => onClick(page - 1)}
dataTest={`${dataTest}-page-previous`}
>
<ChevronLeft />
{translate(previousPageText)}
</Button>
<Button
secondary
className="button-next"
small
disabled={page === pageCount}
onClick={() => onClick(page + 1)}
dataTest={`${dataTest}-page-next`}
>
{translate(nextPageText)}
<ChevronRight />
</Button>
<style jsx>{`
div {
display: flex;
}
div :global(.button-previous) {
height: 32px;
padding-left: 0;
}
div :global(.button-next) {
height: 32px;
padding-right: 0;
margin-left: ${spacers.dp4};
}
`}</style>
</div>
)

PageControls.propTypes = {
dataTest: propTypes.string.isRequired,
nextPageText: propTypes.oneOfType([propTypes.string, propTypes.func])
.isRequired,
page: propTypes.number.isRequired,
pageCount: propTypes.number.isRequired,
previousPageText: propTypes.oneOfType([propTypes.string, propTypes.func])
.isRequired,
onClick: propTypes.func.isRequired,
}

export { PageControls }
52 changes: 52 additions & 0 deletions packages/widgets/src/Pagination/PageSelect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react'
import propTypes from '@dhis2/prop-types'
import { SingleSelect, SingleSelectOption } from '@dhis2/ui-core'
import { spacers } from '@dhis2/ui-constants'

import translate from '../translate'

const createAvailablePages = length =>
Array.from({ length }, (_x, i) => (i + 1).toString())

const PageSelect = ({
dataTest,
pageSelectText,
onChange,
page,
pageCount,
}) => (
<div data-test={`${dataTest}-gotopage`}>
<SingleSelect
dense
selected={page.toString()}
onChange={({ selected }) => onChange(parseInt(selected, 10))}
className="select"
dataTest={`${dataTest}-page-select`}
prefix={translate(pageSelectText)}
>
{createAvailablePages(pageCount).map(availablePage => (
<SingleSelectOption
key={availablePage}
value={availablePage}
label={availablePage}
/>
))}
</SingleSelect>
<style jsx>{`
div {
margin-right: ${spacers.dp12};
}
`}</style>
</div>
)

PageSelect.propTypes = {
dataTest: propTypes.string.isRequired,
page: propTypes.number.isRequired,
pageCount: propTypes.number.isRequired,
pageSelectText: propTypes.oneOfType([propTypes.string, propTypes.func])
.isRequired,
onChange: propTypes.func.isRequired,
}

export { PageSelect, createAvailablePages }
54 changes: 54 additions & 0 deletions packages/widgets/src/Pagination/PageSizeSelect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from 'react'
import propTypes from '@dhis2/prop-types'
import { SingleSelect, SingleSelectOption } from '@dhis2/ui-core'
import { spacers } from '@dhis2/ui-constants'

import translate from '../translate'

const PageSizeSelect = ({
dataTest,
pageSizeSelectText,
pageSize,
pageSizes,
onChange,
}) => (
<div data-test={`${dataTest}-pagesize`}>
<SingleSelect
dense
selected={pageSize.toString()}
onChange={({ selected }) => onChange(parseInt(selected, 10))}
className="select"
dataTest={`${dataTest}-pagesize-select`}
prefix={translate(pageSizeSelectText)}
>
{pageSizes.map(length => (
<SingleSelectOption
key={length}
value={length}
label={length}
/>
))}
</SingleSelect>
<style jsx>{`
div {
display: flex;
align-items: center;
flex-shrink: 0;
min-height: 32px;
margin-right: ${spacers.dp12};
flex-grow: 1;
}
`}</style>
</div>
)

PageSizeSelect.propTypes = {
dataTest: propTypes.string.isRequired,
pageSize: propTypes.number.isRequired,
pageSizeSelectText: propTypes.oneOfType([propTypes.string, propTypes.func])
.isRequired,
pageSizes: propTypes.arrayOf(propTypes.string).isRequired,
onChange: propTypes.func.isRequired,
}

export { PageSizeSelect }
75 changes: 75 additions & 0 deletions packages/widgets/src/Pagination/PageSummary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React from 'react'
import propTypes from '@dhis2/prop-types'
import { colors, spacers } from '@dhis2/ui-constants'

import translate from '../translate'

const getItemRange = (page, pageSize, total) => {
let firstItem, lastItem

if (total === 0) {
// if no items are found, the pager total is 0
// and we want to force the first and last item to be 0 too
firstItem = 0
lastItem = 0
} else {
// page is 1-based
firstItem = (page - 1) * pageSize + 1
lastItem = firstItem + pageSize - 1
}

if (lastItem > total) {
lastItem = total
}

return { firstItem, lastItem }
}

const PageSummary = ({
dataTest,
page,
pageCount,
pageSize,
pageSummaryText,
total,
}) => {
const { firstItem, lastItem } = getItemRange(page, pageSize, total)
const summary = translate(pageSummaryText, {
page,
pageCount,
firstItem,
lastItem,
total,
})

return (
<div data-test={`${dataTest}-summary`}>
<span>{summary}</span>
<style jsx>{`
div {
display: flex;
align-items: center;
flex-shrink: 0;
min-height: 32px;
margin-right: ${spacers.dp12};
}
span {
color: ${colors.grey700};
font-size: 14px;
}
`}</style>
</div>
)
}

PageSummary.propTypes = {
dataTest: propTypes.string.isRequired,
page: propTypes.number.isRequired,
pageCount: propTypes.number.isRequired,
pageSize: propTypes.number.isRequired,
pageSummaryText: propTypes.oneOfType([propTypes.string, propTypes.func])
.isRequired,
total: propTypes.number.isRequired,
}

export { PageSummary, getItemRange }
Loading

0 comments on commit 3d46b78

Please sign in to comment.