@dhis2-ui/pagination 10.16.2 → 10.16.3-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -6
- package/src/__fixtures__/index.js +46 -0
- package/src/__tests__/get-item-range.test.js +65 -0
- package/src/__tests__/page-controls.test.js +80 -0
- package/src/__tests__/page-select.test.js +40 -0
- package/src/__tests__/page-size-select.test.js +29 -0
- package/src/__tests__/page-summary.test.js +70 -0
- package/src/__tests__/pagination.test.js +212 -0
- package/src/get-default-page-summary-text.js +34 -0
- package/src/get-item-range.js +35 -0
- package/src/index.js +1 -0
- package/src/locales/ar/translations.json +10 -0
- package/src/locales/ar_IQ/translations.json +10 -0
- package/src/locales/bn/translations.json +10 -0
- package/src/locales/ckb/translations.json +10 -0
- package/src/locales/cs/translations.json +10 -0
- package/src/locales/da/translations.json +10 -0
- package/src/locales/en/translations.json +10 -0
- package/src/locales/es/translations.json +10 -0
- package/src/locales/es_419/translations.json +10 -0
- package/src/locales/fr/translations.json +10 -0
- package/src/locales/hi_IN/translations.json +10 -0
- package/src/locales/id/translations.json +10 -0
- package/src/locales/index.js +82 -0
- package/src/locales/km/translations.json +10 -0
- package/src/locales/lo/translations.json +10 -0
- package/src/locales/my/translations.json +10 -0
- package/src/locales/nb/translations.json +10 -0
- package/src/locales/nl/translations.json +10 -0
- package/src/locales/prs/translations.json +10 -0
- package/src/locales/ps/translations.json +10 -0
- package/src/locales/pt/translations.json +10 -0
- package/src/locales/pt_BR/translations.json +10 -0
- package/src/locales/ro/translations.json +10 -0
- package/src/locales/ru/translations.json +10 -0
- package/src/locales/sv/translations.json +10 -0
- package/src/locales/tet/translations.json +10 -0
- package/src/locales/tg/translations.json +10 -0
- package/src/locales/uk/translations.json +10 -0
- package/src/locales/ur/translations.json +10 -0
- package/src/locales/uz_Latn/translations.json +10 -0
- package/src/locales/uz_UZ_Cyrl/translations.json +10 -0
- package/src/locales/uz_UZ_Latn/translations.json +10 -0
- package/src/locales/vi/translations.json +10 -0
- package/src/locales/zh/translations.json +10 -0
- package/src/locales/zh_CN/translations.json +10 -0
- package/src/page-controls.js +83 -0
- package/src/page-select.js +62 -0
- package/src/page-size-select.js +64 -0
- package/src/page-summary.js +67 -0
- package/src/pagination.js +152 -0
- package/src/pagination.prod.stories.js +113 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { spacers } from '@dhis2/ui-constants'
|
|
2
|
+
import { SingleSelect, SingleSelectOption } from '@dhis2-ui/select'
|
|
3
|
+
import PropTypes from 'prop-types'
|
|
4
|
+
import React from 'react'
|
|
5
|
+
|
|
6
|
+
// TODO: i18n translate
|
|
7
|
+
const translate = (prop, interpolationObject) => {
|
|
8
|
+
if (typeof prop === 'function') {
|
|
9
|
+
return prop(interpolationObject)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return prop
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const PageSizeSelect = ({
|
|
16
|
+
dataTest,
|
|
17
|
+
disabled,
|
|
18
|
+
pageSizeSelectText,
|
|
19
|
+
pageSize,
|
|
20
|
+
pageSizes,
|
|
21
|
+
onChange,
|
|
22
|
+
}) => (
|
|
23
|
+
<div data-test={`${dataTest}-pagesize`}>
|
|
24
|
+
<SingleSelect
|
|
25
|
+
dense
|
|
26
|
+
disabled={disabled}
|
|
27
|
+
selected={pageSize.toString()}
|
|
28
|
+
onChange={({ selected }) => onChange(parseInt(selected, 10))}
|
|
29
|
+
className="select"
|
|
30
|
+
dataTest={`${dataTest}-pagesize-select`}
|
|
31
|
+
prefix={translate(pageSizeSelectText)}
|
|
32
|
+
>
|
|
33
|
+
{pageSizes.map((length) => (
|
|
34
|
+
<SingleSelectOption
|
|
35
|
+
key={length}
|
|
36
|
+
value={length}
|
|
37
|
+
label={length}
|
|
38
|
+
/>
|
|
39
|
+
))}
|
|
40
|
+
</SingleSelect>
|
|
41
|
+
<style jsx>{`
|
|
42
|
+
div {
|
|
43
|
+
display: flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
flex-shrink: 0;
|
|
46
|
+
min-height: 32px;
|
|
47
|
+
margin-inline-end: ${spacers.dp12};
|
|
48
|
+
flex-grow: 1;
|
|
49
|
+
}
|
|
50
|
+
`}</style>
|
|
51
|
+
</div>
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
PageSizeSelect.propTypes = {
|
|
55
|
+
dataTest: PropTypes.string.isRequired,
|
|
56
|
+
pageSize: PropTypes.number.isRequired,
|
|
57
|
+
pageSizeSelectText: PropTypes.oneOfType([PropTypes.string, PropTypes.func])
|
|
58
|
+
.isRequired,
|
|
59
|
+
pageSizes: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
60
|
+
onChange: PropTypes.func.isRequired,
|
|
61
|
+
disabled: PropTypes.bool,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { PageSizeSelect }
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { colors, spacers } from '@dhis2/ui-constants'
|
|
2
|
+
import cx from 'classnames'
|
|
3
|
+
import PropTypes from 'prop-types'
|
|
4
|
+
import React from 'react'
|
|
5
|
+
|
|
6
|
+
const translate = (prop, interpolationObject) => {
|
|
7
|
+
if (typeof prop === 'function') {
|
|
8
|
+
return prop(interpolationObject)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return prop
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const PageSummary = ({
|
|
15
|
+
dataTest,
|
|
16
|
+
firstItem,
|
|
17
|
+
inactive,
|
|
18
|
+
lastItem,
|
|
19
|
+
page,
|
|
20
|
+
pageCount,
|
|
21
|
+
pageSummaryText,
|
|
22
|
+
total,
|
|
23
|
+
}) => {
|
|
24
|
+
const summary = translate(pageSummaryText, {
|
|
25
|
+
firstItem,
|
|
26
|
+
lastItem,
|
|
27
|
+
page,
|
|
28
|
+
pageCount,
|
|
29
|
+
total,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div data-test={`${dataTest}-summary`}>
|
|
34
|
+
<span className={cx({ inactive })}>{summary}</span>
|
|
35
|
+
<style jsx>{`
|
|
36
|
+
div {
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
flex-shrink: 0;
|
|
40
|
+
min-height: 32px;
|
|
41
|
+
margin-inline-end: ${spacers.dp12};
|
|
42
|
+
}
|
|
43
|
+
span {
|
|
44
|
+
color: ${colors.grey700};
|
|
45
|
+
font-size: 14px;
|
|
46
|
+
}
|
|
47
|
+
span.inactive {
|
|
48
|
+
color: ${colors.grey500};
|
|
49
|
+
}
|
|
50
|
+
`}</style>
|
|
51
|
+
</div>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
PageSummary.propTypes = {
|
|
56
|
+
dataTest: PropTypes.string.isRequired,
|
|
57
|
+
page: PropTypes.number.isRequired,
|
|
58
|
+
pageSummaryText: PropTypes.oneOfType([PropTypes.string, PropTypes.func])
|
|
59
|
+
.isRequired,
|
|
60
|
+
firstItem: PropTypes.number,
|
|
61
|
+
inactive: PropTypes.bool,
|
|
62
|
+
lastItem: PropTypes.number,
|
|
63
|
+
pageCount: PropTypes.number,
|
|
64
|
+
total: PropTypes.number,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { PageSummary }
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { requiredIf } from '@dhis2/prop-types'
|
|
2
|
+
import { spacers } from '@dhis2/ui-constants'
|
|
3
|
+
import cx from 'classnames'
|
|
4
|
+
import PropTypes from 'prop-types'
|
|
5
|
+
import React from 'react'
|
|
6
|
+
import { getDefaultPageSummaryText } from './get-default-page-summary-text.js'
|
|
7
|
+
import { getItemRange } from './get-item-range.js'
|
|
8
|
+
import i18n from './locales/index.js'
|
|
9
|
+
import { PageControls } from './page-controls.js'
|
|
10
|
+
import { PageSelect } from './page-select.js'
|
|
11
|
+
import { PageSizeSelect } from './page-size-select.js'
|
|
12
|
+
import { PageSummary } from './page-summary.js'
|
|
13
|
+
|
|
14
|
+
const MAX_PAGE_COUNT = 2000
|
|
15
|
+
|
|
16
|
+
const defaultProps = {
|
|
17
|
+
dataTest: 'dhis2-uiwidgets-pagination',
|
|
18
|
+
nextPageText: () => i18n.t('Next'),
|
|
19
|
+
pageSelectText: () => i18n.t('Page'),
|
|
20
|
+
pageSizes: ['5', '10', '20', '30', '40', '50', '75', '100'],
|
|
21
|
+
pageSizeSelectText: () => i18n.t('Items per page'),
|
|
22
|
+
pageSummaryText: getDefaultPageSummaryText,
|
|
23
|
+
previousPageText: () => i18n.t('Previous'),
|
|
24
|
+
}
|
|
25
|
+
const Pagination = ({
|
|
26
|
+
className,
|
|
27
|
+
dataTest = defaultProps.dataTest,
|
|
28
|
+
disabled,
|
|
29
|
+
hidePageSelect,
|
|
30
|
+
hidePageSizeSelect,
|
|
31
|
+
hidePageSummary,
|
|
32
|
+
isLastPage,
|
|
33
|
+
nextPageText = defaultProps.nextPageText,
|
|
34
|
+
onPageChange,
|
|
35
|
+
onPageSizeChange,
|
|
36
|
+
page,
|
|
37
|
+
pageCount,
|
|
38
|
+
pageLength,
|
|
39
|
+
pageSelectText = defaultProps.pageSelectText,
|
|
40
|
+
pageSize,
|
|
41
|
+
pageSizes = defaultProps.pageSizes,
|
|
42
|
+
pageSizeSelectText = defaultProps.pageSizeSelectText,
|
|
43
|
+
pageSummaryText = defaultProps.pageSummaryText,
|
|
44
|
+
previousPageText = defaultProps.previousPageText,
|
|
45
|
+
total,
|
|
46
|
+
}) => {
|
|
47
|
+
const { firstItem, lastItem } = getItemRange({
|
|
48
|
+
isLastPage,
|
|
49
|
+
page,
|
|
50
|
+
pageLength,
|
|
51
|
+
pageSize,
|
|
52
|
+
total,
|
|
53
|
+
})
|
|
54
|
+
const showPageSelect =
|
|
55
|
+
!hidePageSelect &&
|
|
56
|
+
typeof pageCount === 'number' &&
|
|
57
|
+
pageCount > 1 &&
|
|
58
|
+
pageCount <= MAX_PAGE_COUNT
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div className={cx('container', className)} data-test={dataTest}>
|
|
62
|
+
{hidePageSizeSelect ? (
|
|
63
|
+
<div className="spacer" />
|
|
64
|
+
) : (
|
|
65
|
+
<PageSizeSelect
|
|
66
|
+
dataTest={dataTest}
|
|
67
|
+
disabled={disabled}
|
|
68
|
+
pageSize={pageSize}
|
|
69
|
+
pageSizes={pageSizes}
|
|
70
|
+
onChange={onPageSizeChange}
|
|
71
|
+
pageSizeSelectText={pageSizeSelectText}
|
|
72
|
+
/>
|
|
73
|
+
)}
|
|
74
|
+
{!hidePageSummary && (
|
|
75
|
+
<PageSummary
|
|
76
|
+
dataTest={dataTest}
|
|
77
|
+
inactive={disabled}
|
|
78
|
+
firstItem={firstItem}
|
|
79
|
+
lastItem={lastItem}
|
|
80
|
+
page={page}
|
|
81
|
+
pageCount={pageCount}
|
|
82
|
+
pageSummaryText={pageSummaryText}
|
|
83
|
+
total={total}
|
|
84
|
+
/>
|
|
85
|
+
)}
|
|
86
|
+
<div className="page-navigation">
|
|
87
|
+
{showPageSelect && (
|
|
88
|
+
<PageSelect
|
|
89
|
+
dataTest={dataTest}
|
|
90
|
+
disabled={disabled}
|
|
91
|
+
pageSelectText={pageSelectText}
|
|
92
|
+
page={page}
|
|
93
|
+
pageCount={pageCount}
|
|
94
|
+
onChange={onPageChange}
|
|
95
|
+
/>
|
|
96
|
+
)}
|
|
97
|
+
<PageControls
|
|
98
|
+
dataTest={dataTest}
|
|
99
|
+
nextPageText={nextPageText}
|
|
100
|
+
page={page}
|
|
101
|
+
previousPageText={previousPageText}
|
|
102
|
+
onClick={onPageChange}
|
|
103
|
+
isNextDisabled={
|
|
104
|
+
disabled || isLastPage || page === pageCount
|
|
105
|
+
}
|
|
106
|
+
isPreviousDisabled={disabled || page === 1}
|
|
107
|
+
/>
|
|
108
|
+
</div>
|
|
109
|
+
<style jsx>{`
|
|
110
|
+
.container {
|
|
111
|
+
display: flex;
|
|
112
|
+
flex-wrap: wrap;
|
|
113
|
+
row-gap: ${spacers.dp4};
|
|
114
|
+
}
|
|
115
|
+
.spacer {
|
|
116
|
+
flex-grow: 1;
|
|
117
|
+
}
|
|
118
|
+
.page-navigation {
|
|
119
|
+
display: flex;
|
|
120
|
+
align-items: center;
|
|
121
|
+
flex-shrink: 0;
|
|
122
|
+
min-height: 32px;
|
|
123
|
+
}
|
|
124
|
+
`}</style>
|
|
125
|
+
</div>
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
Pagination.propTypes = {
|
|
130
|
+
page: PropTypes.number.isRequired,
|
|
131
|
+
pageSize: PropTypes.number.isRequired,
|
|
132
|
+
className: PropTypes.string,
|
|
133
|
+
dataTest: PropTypes.string,
|
|
134
|
+
disabled: PropTypes.bool,
|
|
135
|
+
hidePageSelect: PropTypes.bool,
|
|
136
|
+
hidePageSizeSelect: PropTypes.bool,
|
|
137
|
+
hidePageSummary: PropTypes.bool,
|
|
138
|
+
isLastPage: PropTypes.bool,
|
|
139
|
+
nextPageText: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
|
140
|
+
pageCount: PropTypes.number,
|
|
141
|
+
pageLength: requiredIf((props) => props.isLastPage, PropTypes.number),
|
|
142
|
+
pageSelectText: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
|
143
|
+
pageSizeSelectText: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
|
144
|
+
pageSizes: PropTypes.arrayOf(PropTypes.string),
|
|
145
|
+
pageSummaryText: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
|
146
|
+
previousPageText: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
|
|
147
|
+
total: PropTypes.number,
|
|
148
|
+
onPageChange: PropTypes.func,
|
|
149
|
+
onPageSizeChange: PropTypes.func,
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export { Pagination }
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import i18n from '@dhis2/d2-i18n'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import * as pagers from './__fixtures__/index.js'
|
|
4
|
+
import { Pagination } from './pagination.js'
|
|
5
|
+
|
|
6
|
+
const subtitle = 'Allows navigation through data displayed over several pages'
|
|
7
|
+
|
|
8
|
+
const description = `
|
|
9
|
+
Pagination allows data to be split in pages. Paging large amounts of data avoids overwhelming users and should always be used wherever a lot of data is displayed. Pagination controls allow a user to browse through a set of data or navigate to a specific page depending on the type of pagination used.
|
|
10
|
+
|
|
11
|
+
**Do not rely on pagination for navigating datasets. A user should be able to search within, sort and filter datasets too, rather than needing to click through many pages looking for the right data item.**
|
|
12
|
+
|
|
13
|
+
\`\`\`js
|
|
14
|
+
import { Pagination } from '@dhis2/ui'
|
|
15
|
+
\`\`\`
|
|
16
|
+
|
|
17
|
+
_**Note**: Dropdown menus may not display properly on this page. View these demos in the 'Canvas' tab._
|
|
18
|
+
`
|
|
19
|
+
|
|
20
|
+
const logOnPageChange = (page) => {
|
|
21
|
+
console.log(`Now navigate to page ${page}...`)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const logOnPageSizeChange = (pageSize) => {
|
|
25
|
+
console.log(`Now change page size to ${pageSize}...`)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default {
|
|
29
|
+
title: 'Pagination',
|
|
30
|
+
component: Pagination,
|
|
31
|
+
parameters: {
|
|
32
|
+
componentSubtitle: subtitle,
|
|
33
|
+
docs: { description: { component: description } },
|
|
34
|
+
},
|
|
35
|
+
// Default args for stories
|
|
36
|
+
args: {
|
|
37
|
+
onPageChange: logOnPageChange,
|
|
38
|
+
onPageSizeChange: logOnPageSizeChange,
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const Template = (args) => <Pagination {...args} />
|
|
43
|
+
|
|
44
|
+
export const Default = Template.bind({})
|
|
45
|
+
Default.args = { ...pagers.atTenthPage }
|
|
46
|
+
|
|
47
|
+
export const Disabled = Template.bind({})
|
|
48
|
+
Disabled.args = { ...pagers.atTenthPage, disabled: true }
|
|
49
|
+
|
|
50
|
+
export const PagerAtFirstPage = Template.bind({})
|
|
51
|
+
PagerAtFirstPage.args = { ...pagers.atFirstPage }
|
|
52
|
+
|
|
53
|
+
export const PagerAtLastPage = Template.bind({})
|
|
54
|
+
PagerAtLastPage.args = { ...pagers.atLastPage }
|
|
55
|
+
|
|
56
|
+
export const NoTotal = Template.bind({})
|
|
57
|
+
NoTotal.args = { ...pagers.noTotal }
|
|
58
|
+
|
|
59
|
+
export const NoTotalAtLastPage = Template.bind({})
|
|
60
|
+
NoTotalAtLastPage.args = { ...pagers.noTotalAtLastPage, pageLength: 26 }
|
|
61
|
+
|
|
62
|
+
export const NoTotalAtLastPageWithoutPageLength = Template.bind({})
|
|
63
|
+
NoTotalAtLastPageWithoutPageLength.args = { ...pagers.noTotalAtLastPage }
|
|
64
|
+
|
|
65
|
+
export const WithoutPageSizeSelect = Template.bind({})
|
|
66
|
+
WithoutPageSizeSelect.args = {
|
|
67
|
+
...pagers.atTenthPage,
|
|
68
|
+
hidePageSizeSelect: true,
|
|
69
|
+
hidePageSelect: false,
|
|
70
|
+
hidePageSummary: false,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const WithoutGoToPageSelect = Template.bind({})
|
|
74
|
+
WithoutGoToPageSelect.args = { ...pagers.atTenthPage, hidePageSelect: true }
|
|
75
|
+
|
|
76
|
+
export const WithoutPageSummary = Template.bind({})
|
|
77
|
+
WithoutPageSummary.args = { ...pagers.atTenthPage, hidePageSummary: true }
|
|
78
|
+
|
|
79
|
+
export const WithCustomPageSummary = Template.bind({})
|
|
80
|
+
WithCustomPageSummary.args = {
|
|
81
|
+
...pagers.atTenthPage,
|
|
82
|
+
pageSummaryText: (interpolationObject) =>
|
|
83
|
+
i18n.t(
|
|
84
|
+
'You are at page {{page}} showing items {{firstItem}}-{{lastItem}}, but there are {{pageCount}} pages and {{total}} items',
|
|
85
|
+
interpolationObject
|
|
86
|
+
),
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const WithoutAnySelect = Template.bind({})
|
|
90
|
+
WithoutAnySelect.args = {
|
|
91
|
+
...pagers.atTenthPage,
|
|
92
|
+
...WithoutGoToPageSelect.args,
|
|
93
|
+
...WithoutPageSizeSelect.args,
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const InBox = ({ boxWidth, ...args }) => (
|
|
97
|
+
<div style={{ width: boxWidth }}>
|
|
98
|
+
<Pagination {...args} />
|
|
99
|
+
</div>
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
export const MediumWidth = InBox.bind({})
|
|
103
|
+
MediumWidth.args = { ...pagers.atTenthPage, boxWidth: 500 }
|
|
104
|
+
|
|
105
|
+
export const NarrowWidth = InBox.bind({})
|
|
106
|
+
NarrowWidth.args = { ...pagers.atTenthPage, boxWidth: 100 }
|
|
107
|
+
|
|
108
|
+
export const RTL = (args) => (
|
|
109
|
+
<div dir="rtl">
|
|
110
|
+
<Pagination {...args} />
|
|
111
|
+
</div>
|
|
112
|
+
)
|
|
113
|
+
RTL.args = { ...pagers.atTenthPage }
|