@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.
Files changed (52) hide show
  1. package/package.json +7 -6
  2. package/src/__fixtures__/index.js +46 -0
  3. package/src/__tests__/get-item-range.test.js +65 -0
  4. package/src/__tests__/page-controls.test.js +80 -0
  5. package/src/__tests__/page-select.test.js +40 -0
  6. package/src/__tests__/page-size-select.test.js +29 -0
  7. package/src/__tests__/page-summary.test.js +70 -0
  8. package/src/__tests__/pagination.test.js +212 -0
  9. package/src/get-default-page-summary-text.js +34 -0
  10. package/src/get-item-range.js +35 -0
  11. package/src/index.js +1 -0
  12. package/src/locales/ar/translations.json +10 -0
  13. package/src/locales/ar_IQ/translations.json +10 -0
  14. package/src/locales/bn/translations.json +10 -0
  15. package/src/locales/ckb/translations.json +10 -0
  16. package/src/locales/cs/translations.json +10 -0
  17. package/src/locales/da/translations.json +10 -0
  18. package/src/locales/en/translations.json +10 -0
  19. package/src/locales/es/translations.json +10 -0
  20. package/src/locales/es_419/translations.json +10 -0
  21. package/src/locales/fr/translations.json +10 -0
  22. package/src/locales/hi_IN/translations.json +10 -0
  23. package/src/locales/id/translations.json +10 -0
  24. package/src/locales/index.js +82 -0
  25. package/src/locales/km/translations.json +10 -0
  26. package/src/locales/lo/translations.json +10 -0
  27. package/src/locales/my/translations.json +10 -0
  28. package/src/locales/nb/translations.json +10 -0
  29. package/src/locales/nl/translations.json +10 -0
  30. package/src/locales/prs/translations.json +10 -0
  31. package/src/locales/ps/translations.json +10 -0
  32. package/src/locales/pt/translations.json +10 -0
  33. package/src/locales/pt_BR/translations.json +10 -0
  34. package/src/locales/ro/translations.json +10 -0
  35. package/src/locales/ru/translations.json +10 -0
  36. package/src/locales/sv/translations.json +10 -0
  37. package/src/locales/tet/translations.json +10 -0
  38. package/src/locales/tg/translations.json +10 -0
  39. package/src/locales/uk/translations.json +10 -0
  40. package/src/locales/ur/translations.json +10 -0
  41. package/src/locales/uz_Latn/translations.json +10 -0
  42. package/src/locales/uz_UZ_Cyrl/translations.json +10 -0
  43. package/src/locales/uz_UZ_Latn/translations.json +10 -0
  44. package/src/locales/vi/translations.json +10 -0
  45. package/src/locales/zh/translations.json +10 -0
  46. package/src/locales/zh_CN/translations.json +10 -0
  47. package/src/page-controls.js +83 -0
  48. package/src/page-select.js +62 -0
  49. package/src/page-size-select.js +64 -0
  50. package/src/page-summary.js +67 -0
  51. package/src/pagination.js +152 -0
  52. 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 }