diff --git a/web/src/__tests__/components/DatasetsPagination.test.tsx b/web/src/__tests__/components/DatasetsPagination.test.tsx new file mode 100644 index 0000000000..5899128fd4 --- /dev/null +++ b/web/src/__tests__/components/DatasetsPagination.test.tsx @@ -0,0 +1,21 @@ +import * as React from 'react' +import { mount } from 'enzyme' +import Datasets from '../../routes/datasets/Datasets' +import { Tooltip } from '@material-ui/core' +import IconButton from '@material-ui/core/IconButton' + +test.skip('DatasetsPagination Component', () => { + + const wrapper = mount() + + it('should render', () => { + expect(wrapper.exists()).toBe(true) + }) + + it('should find Tooltip elements containing IconButton elements', () => { + expect( + wrapper + .find(Tooltip) + ).toContain(IconButton) + }) +}) \ No newline at end of file diff --git a/web/src/routes/datasets/Datasets.tsx b/web/src/routes/datasets/Datasets.tsx index 97ca472508..1eb0200822 100644 --- a/web/src/routes/datasets/Datasets.tsx +++ b/web/src/routes/datasets/Datasets.tsx @@ -2,7 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 import * as Redux from 'redux' -import { Container, Table, TableBody, TableCell, TableHead, TableRow } from '@material-ui/core' +import { Container, Table, TableBody, TableCell, TableHead, TableRow, Theme, Tooltip } from '@material-ui/core' +import { ChevronLeftRounded, ChevronRightRounded } from '@material-ui/icons' +import IconButton from '@material-ui/core/IconButton' import { Dataset } from '../../types/api' import { IState } from '../../store/reducers' import { MqScreenLoad } from '../../components/core/screen-load/MqScreenLoad' @@ -20,7 +22,13 @@ import React from 'react' import createStyles from '@material-ui/core/styles/createStyles' import withStyles, { WithStyles } from '@material-ui/core/styles/withStyles' -const styles = () => createStyles({}) +const PAGE_SIZE = 20 + +const styles = (theme: Theme) => createStyles({ + ml2: { + marginLeft: theme.spacing(2) + } +}) interface StateProps { datasets: Dataset[] @@ -34,21 +42,47 @@ interface DispatchProps { resetDatasets: typeof resetDatasets } +interface DatasetsState { + datasets: Dataset[] + page: number + pageIsLast: boolean +} + type DatasetsProps = WithStyles & StateProps & DispatchProps -class Datasets extends React.Component { +class Datasets extends React.Component { + + constructor(props: DatasetsProps) { + super(props) + this.state = { + datasets: [], + page: 1, + pageIsLast: false + } + } + componentDidMount() { if (this.props.selectedNamespace) { - this.props.fetchDatasets(this.props.selectedNamespace) + this.props.fetchDatasets(this.props.selectedNamespace, PAGE_SIZE) } } componentDidUpdate(prevProps: Readonly) { + const { datasets: datasetsState, page } = this.state + const { datasets: datasetsProps } = this.props + if ( prevProps.selectedNamespace !== this.props.selectedNamespace && this.props.selectedNamespace ) { - this.props.fetchDatasets(this.props.selectedNamespace) + this.props.fetchDatasets(this.props.selectedNamespace, PAGE_SIZE) + } + + if (datasetsProps !== datasetsState) { + this.setState({ + datasets: datasetsProps, + pageIsLast: datasetsProps.length < page * PAGE_SIZE + }) } } @@ -56,9 +90,43 @@ class Datasets extends React.Component { this.props.resetDatasets() } + getDatasets() { + const { datasets, page } = this.state + return datasets.slice((page - 1) * PAGE_SIZE, PAGE_SIZE + (page - 1) * PAGE_SIZE) + } + + pageNavigation() { + const { datasets, page, pageIsLast } = this.state + const titlePos = datasets.length < PAGE_SIZE && page === 1 + ? `1 - ${datasets.length}` + : datasets.length > PAGE_SIZE && page === 1 + ? `1 - ${PAGE_SIZE}` + : datasets.length && page > 1 && pageIsLast === false + ? `${PAGE_SIZE * page - PAGE_SIZE + 1} - ${PAGE_SIZE * page}` + : datasets.length && page > 1 && pageIsLast + ? `${PAGE_SIZE * page - PAGE_SIZE + 1} - ${datasets.length}` + : `${datasets.length}` + return `${page} (${titlePos})` + } + + handleClickPage(direction: 'prev' | 'next') { + const { page } = this.state + const directionPage = direction === 'next' ? page + 1 : page - 1 + + if (this.props.selectedNamespace) { + this.props.fetchDatasets( + this.props.selectedNamespace, + PAGE_SIZE * directionPage + ) + } + this.setState({ page: directionPage }) + } + render() { - const { datasets, isDatasetsLoading, isDatasetsInit } = this.props + const { isDatasetsLoading, isDatasetsInit, classes } = this.props + const { datasets, page, pageIsLast } = this.state const i18next = require('i18next') + return ( @@ -68,11 +136,35 @@ class Datasets extends React.Component { {i18next.t('datasets_route.empty_body')} - + ) : ( <> - - {i18next.t('datasets_route.heading')} + + + {i18next.t('datasets_route.heading')} + Page: {this.pageNavigation()} + + + + this.handleClickPage('prev')} + > + + + + + this.handleClickPage('next')} + > + + + + @@ -95,7 +187,7 @@ class Datasets extends React.Component { - {datasets + {this.getDatasets() .filter(dataset => !dataset.deleted) .map(dataset => { return ( @@ -137,6 +229,27 @@ class Datasets extends React.Component {
)} + + + this.handleClickPage('prev')} + > + + + + + this.handleClickPage('next')} + > + + + +
@@ -160,4 +273,4 @@ const mapDispatchToProps = (dispatch: Redux.Dispatch) => dispatch ) -export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(Datasets)) +export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(Datasets)) \ No newline at end of file diff --git a/web/src/store/actionCreators/index.ts b/web/src/store/actionCreators/index.ts index 4e8c5043c9..520748ae42 100644 --- a/web/src/store/actionCreators/index.ts +++ b/web/src/store/actionCreators/index.ts @@ -36,10 +36,11 @@ export const resetEvents = () => ({ type: actionTypes.RESET_EVENTS }) -export const fetchDatasets = (namespace: string) => ({ +export const fetchDatasets = (namespace: string, limit: number) => ({ type: actionTypes.FETCH_DATASETS, payload: { - namespace + namespace, + limit } })