Skip to content

Commit a7cf7fb

Browse files
authored
add pagination to datasets (#2512)
* add pagination to datasets --------- Signed-off-by: Michael Robinson <[email protected]>
1 parent b62cee3 commit a7cf7fb

File tree

3 files changed

+148
-13
lines changed

3 files changed

+148
-13
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import * as React from 'react'
2+
import { mount } from 'enzyme'
3+
import Datasets from '../../routes/datasets/Datasets'
4+
import { Tooltip } from '@material-ui/core'
5+
import IconButton from '@material-ui/core/IconButton'
6+
7+
test.skip('DatasetsPagination Component', () => {
8+
9+
const wrapper = mount(<Datasets />)
10+
11+
it('should render', () => {
12+
expect(wrapper.exists()).toBe(true)
13+
})
14+
15+
it('should find Tooltip elements containing IconButton elements', () => {
16+
expect(
17+
wrapper
18+
.find(Tooltip)
19+
).toContain(IconButton)
20+
})
21+
})

web/src/routes/datasets/Datasets.tsx

Lines changed: 124 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
import * as Redux from 'redux'
5-
import { Container, Table, TableBody, TableCell, TableHead, TableRow } from '@material-ui/core'
5+
import { Container, Table, TableBody, TableCell, TableHead, TableRow, Theme, Tooltip } from '@material-ui/core'
6+
import { ChevronLeftRounded, ChevronRightRounded } from '@material-ui/icons'
7+
import IconButton from '@material-ui/core/IconButton'
68
import { Dataset } from '../../types/api'
79
import { IState } from '../../store/reducers'
810
import { MqScreenLoad } from '../../components/core/screen-load/MqScreenLoad'
@@ -20,7 +22,13 @@ import React from 'react'
2022
import createStyles from '@material-ui/core/styles/createStyles'
2123
import withStyles, { WithStyles } from '@material-ui/core/styles/withStyles'
2224

23-
const styles = () => createStyles({})
25+
const PAGE_SIZE = 20
26+
27+
const styles = (theme: Theme) => createStyles({
28+
ml2: {
29+
marginLeft: theme.spacing(2)
30+
}
31+
})
2432

2533
interface StateProps {
2634
datasets: Dataset[]
@@ -34,31 +42,91 @@ interface DispatchProps {
3442
resetDatasets: typeof resetDatasets
3543
}
3644

45+
interface DatasetsState {
46+
datasets: Dataset[]
47+
page: number
48+
pageIsLast: boolean
49+
}
50+
3751
type DatasetsProps = WithStyles<typeof styles> & StateProps & DispatchProps
3852

39-
class Datasets extends React.Component<DatasetsProps> {
53+
class Datasets extends React.Component<DatasetsProps, DatasetsState> {
54+
55+
constructor(props: DatasetsProps) {
56+
super(props)
57+
this.state = {
58+
datasets: [],
59+
page: 1,
60+
pageIsLast: false
61+
}
62+
}
63+
4064
componentDidMount() {
4165
if (this.props.selectedNamespace) {
42-
this.props.fetchDatasets(this.props.selectedNamespace)
66+
this.props.fetchDatasets(this.props.selectedNamespace, PAGE_SIZE)
4367
}
4468
}
4569

4670
componentDidUpdate(prevProps: Readonly<DatasetsProps>) {
71+
const { datasets: datasetsState, page } = this.state
72+
const { datasets: datasetsProps } = this.props
73+
4774
if (
4875
prevProps.selectedNamespace !== this.props.selectedNamespace &&
4976
this.props.selectedNamespace
5077
) {
51-
this.props.fetchDatasets(this.props.selectedNamespace)
78+
this.props.fetchDatasets(this.props.selectedNamespace, PAGE_SIZE)
79+
}
80+
81+
if (datasetsProps !== datasetsState) {
82+
this.setState({
83+
datasets: datasetsProps,
84+
pageIsLast: datasetsProps.length < page * PAGE_SIZE
85+
})
5286
}
5387
}
5488

5589
componentWillUnmount() {
5690
this.props.resetDatasets()
5791
}
5892

93+
getDatasets() {
94+
const { datasets, page } = this.state
95+
return datasets.slice((page - 1) * PAGE_SIZE, PAGE_SIZE + (page - 1) * PAGE_SIZE)
96+
}
97+
98+
pageNavigation() {
99+
const { datasets, page, pageIsLast } = this.state
100+
const titlePos = datasets.length < PAGE_SIZE && page === 1
101+
? `1 - ${datasets.length}`
102+
: datasets.length > PAGE_SIZE && page === 1
103+
? `1 - ${PAGE_SIZE}`
104+
: datasets.length && page > 1 && pageIsLast === false
105+
? `${PAGE_SIZE * page - PAGE_SIZE + 1} - ${PAGE_SIZE * page}`
106+
: datasets.length && page > 1 && pageIsLast
107+
? `${PAGE_SIZE * page - PAGE_SIZE + 1} - ${datasets.length}`
108+
: `${datasets.length}`
109+
return `${page} (${titlePos})`
110+
}
111+
112+
handleClickPage(direction: 'prev' | 'next') {
113+
const { page } = this.state
114+
const directionPage = direction === 'next' ? page + 1 : page - 1
115+
116+
if (this.props.selectedNamespace) {
117+
this.props.fetchDatasets(
118+
this.props.selectedNamespace,
119+
PAGE_SIZE * directionPage
120+
)
121+
}
122+
this.setState({ page: directionPage })
123+
}
124+
59125
render() {
60-
const { datasets, isDatasetsLoading, isDatasetsInit } = this.props
126+
const { isDatasetsLoading, isDatasetsInit, classes } = this.props
127+
const { datasets, page, pageIsLast } = this.state
61128
const i18next = require('i18next')
129+
62130
return (
63131
<Container maxWidth={'lg'} disableGutters>
64132
<MqScreenLoad loading={isDatasetsLoading || !isDatasetsInit}>
@@ -68,11 +136,35 @@ class Datasets extends React.Component<DatasetsProps> {
68136
<MqEmpty title={i18next.t('datasets_route.empty_title')}>
69137
<MqText subdued>{i18next.t('datasets_route.empty_body')}</MqText>
70138
</MqEmpty>
71-
</Box>
139+
</Box>
72140
) : (
73141
<>
74-
<Box p={2}>
75-
<MqText heading>{i18next.t('datasets_route.heading')}</MqText>
142+
<Box p={2} display={'flex'} justifyContent={'space-between'}>
143+
<Box>
144+
<MqText heading>{i18next.t('datasets_route.heading')}</MqText>
145+
Page: {this.pageNavigation()}
146+
</Box>
147+
<Box>
148+
<Tooltip title={i18next.t('events_route.previous_page')}>
149+
<IconButton
150+
className={classes.ml2}
151+
color='primary'
152+
disabled={page === 1}
153+
onClick={() => this.handleClickPage('prev')}
154+
>
155+
<ChevronLeftRounded />
156+
</IconButton>
157+
</Tooltip>
158+
<Tooltip title={i18next.t('events_route.next_page')}>
159+
<IconButton
160+
color='primary'
161+
disabled={pageIsLast}
162+
onClick={() => this.handleClickPage('next')}
163+
>
164+
<ChevronRightRounded />
165+
</IconButton>
166+
</Tooltip>
167+
</Box>
76168
</Box>
77169
<Table size='small'>
78170
<TableHead>
@@ -95,7 +187,7 @@ class Datasets extends React.Component<DatasetsProps> {
95187
</TableRow>
96188
</TableHead>
97189
<TableBody>
98-
{datasets
190+
{this.getDatasets()
99191
.filter(dataset => !dataset.deleted)
100192
.map(dataset => {
101193
return (
@@ -137,6 +229,27 @@ class Datasets extends React.Component<DatasetsProps> {
137229
</Table>
138230
</>
139231
)}
232+
<Box display={'flex'} justifyContent={'flex-end'} mb={2}>
233+
<Tooltip title={i18next.t('events_route.previous_page')}>
234+
<IconButton
235+
className={classes.ml2}
236+
color='primary'
237+
disabled={page === 1}
238+
onClick={() => this.handleClickPage('prev')}
239+
>
240+
<ChevronLeftRounded />
241+
</IconButton>
242+
</Tooltip>
243+
<Tooltip title={i18next.t('events_route.next_page')}>
244+
<IconButton
245+
color='primary'
246+
disabled={pageIsLast}
247+
onClick={() => this.handleClickPage('next')}
248+
>
249+
<ChevronRightRounded />
250+
</IconButton>
251+
</Tooltip>
252+
</Box>
140253
</>
141254
</MqScreenLoad>
142255
</Container>
@@ -160,4 +273,4 @@ const mapDispatchToProps = (dispatch: Redux.Dispatch) =>
160273
dispatch
161274
)
162275

163-
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(Datasets))
276+
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(Datasets))

web/src/store/actionCreators/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,11 @@ export const resetEvents = () => ({
3636
type: actionTypes.RESET_EVENTS
3737
})
3838

39-
export const fetchDatasets = (namespace: string) => ({
39+
export const fetchDatasets = (namespace: string, limit: number) => ({
4040
type: actionTypes.FETCH_DATASETS,
4141
payload: {
42-
namespace
42+
namespace,
43+
limit
4344
}
4445
})
4546

0 commit comments

Comments
 (0)