Skip to content

Commit d0d35fa

Browse files
phixMephix
andauthored
Paging on jobs and datasets (#2614)
* Jobs paging. * Datasets paging. * Fixing tests. --------- Co-authored-by: phix <peter.hicks@astronomer.io>
1 parent d1d47e2 commit d0d35fa

12 files changed

Lines changed: 202 additions & 129 deletions

File tree

web/src/__tests__/reducers/datasets.test.ts

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

44
import * as actionTypes from '../../store/actionCreators/actionTypes'
5-
import datasetsReducer, { initialState } from '../../store/reducers/datasets'
5+
import datasetsReducer, {IDatasetsAction, initialState} from '../../store/reducers/datasets'
66

77
const datasets = require('../../../docker/db/data/datasets.json')
88

@@ -11,13 +11,15 @@ describe('datasets reducer', () => {
1111
const action = {
1212
type: actionTypes.FETCH_DATASETS_SUCCESS,
1313
payload: {
14-
datasets: datasets
14+
datasets: datasets,
15+
totalCount: 16
1516
}
16-
}
17+
} as IDatasetsAction
1718
expect(datasetsReducer(initialState, action)).toStrictEqual({
1819
init: true,
1920
isLoading: false,
2021
result: datasets,
22+
totalCount: 16,
2123
deletedDatasetName: ''
2224
})
2325
})

web/src/__tests__/reducers/jobs.test.ts

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

44
import * as actionTypes from '../../store/actionCreators/actionTypes'
5-
import jobsReducer, { initialState } from '../../store/reducers/jobs'
5+
import jobsReducer, {IJobsAction, initialState} from '../../store/reducers/jobs'
66
import { stopWatchDuration } from "../../helpers/time";
7+
import { Job } from "../../types/api";
78

89
const jobs = require('../../../docker/db/data/jobs.json')
910

@@ -13,10 +14,11 @@ describe('jobs reducer', () => {
1314
const action = {
1415
type: actionTypes.FETCH_JOBS_SUCCESS,
1516
payload: {
16-
jobs: jobs
17+
totalCount: 13,
18+
jobs: jobs as Job[]
1719
}
18-
}
19-
expect(jobsReducer(initialState, action)).toStrictEqual({ isLoading: false, result: jobs, init: true, deletedJobName: '' })
20+
} as IJobsAction
21+
expect(jobsReducer(initialState, action)).toStrictEqual({ isLoading: false, result: jobs, totalCount: 13, init: true, deletedJobName: '' })
2022
})
2123
})
2224

web/src/routes/datasets/Datasets.tsx

Lines changed: 58 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import * as Redux from 'redux'
55
import { ChevronLeftRounded, ChevronRightRounded } from '@mui/icons-material'
66
import {
7+
Chip,
78
Container,
89
Table,
910
TableBody,
@@ -35,12 +36,11 @@ interface StateProps {
3536
isDatasetsLoading: boolean
3637
isDatasetsInit: boolean
3738
selectedNamespace: Nullable<string>
39+
totalCount: number
3840
}
3941

4042
interface DatasetsState {
41-
datasets: Dataset[]
4243
page: number
43-
pageIsLast: boolean
4444
}
4545

4646
interface DispatchProps {
@@ -50,52 +50,29 @@ interface DispatchProps {
5050

5151
type DatasetsProps = StateProps & DispatchProps
5252

53+
const PAGE_SIZE = 20
54+
5355
const Datasets: React.FC<DatasetsProps> = ({
5456
datasets,
57+
totalCount,
5558
isDatasetsLoading,
5659
isDatasetsInit,
5760
selectedNamespace,
5861
fetchDatasets,
5962
resetDatasets,
6063
}) => {
61-
const PAGE_SIZE = 20
62-
const mounted = React.useRef<boolean>(false)
63-
const prevSelectedNamespace = React.useRef<Nullable<string>>()
64-
6564
const defaultState = {
66-
datasets: [],
67-
page: 1,
68-
pageIsLast: false,
65+
page: 0,
6966
}
7067
const [state, setState] = React.useState<DatasetsState>(defaultState)
7168

7269
const theme = createTheme(useTheme())
7370

7471
React.useEffect(() => {
75-
if (!mounted.current) {
76-
// on mount
77-
if (selectedNamespace) {
78-
fetchDatasets(selectedNamespace, PAGE_SIZE)
79-
}
80-
mounted.current = true
81-
} else {
82-
// on update
83-
if (prevSelectedNamespace.current !== selectedNamespace && selectedNamespace) {
84-
fetchDatasets(selectedNamespace, PAGE_SIZE)
85-
setState(defaultState)
86-
}
87-
88-
if (datasets !== state.datasets) {
89-
setState({
90-
...state,
91-
datasets,
92-
pageIsLast: datasets.length < state.page * PAGE_SIZE,
93-
})
94-
}
95-
96-
prevSelectedNamespace.current = selectedNamespace
72+
if (selectedNamespace) {
73+
fetchDatasets(selectedNamespace, PAGE_SIZE, state.page * PAGE_SIZE)
9774
}
98-
})
75+
}, [selectedNamespace, state.page])
9976

10077
React.useEffect(() => {
10178
return () => {
@@ -104,28 +81,13 @@ const Datasets: React.FC<DatasetsProps> = ({
10481
}
10582
}, [])
10683

107-
const pageNavigation = () => {
108-
const { datasets, page, pageIsLast } = state
109-
const titlePos =
110-
datasets.length < PAGE_SIZE && page === 1
111-
? `1 - ${datasets.length}`
112-
: datasets.length > PAGE_SIZE && page === 1
113-
? `1 - ${PAGE_SIZE}`
114-
: datasets.length && page > 1 && pageIsLast === false
115-
? `${PAGE_SIZE * page - PAGE_SIZE + 1} - ${PAGE_SIZE * page}`
116-
: datasets.length && page > 1 && pageIsLast
117-
? `${PAGE_SIZE * page - PAGE_SIZE + 1} - ${datasets.length}`
118-
: `${datasets.length}`
119-
return `${page} (${titlePos})`
120-
}
121-
12284
const handleClickPage = (direction: 'prev' | 'next') => {
12385
const directionPage = direction === 'next' ? state.page + 1 : state.page - 1
12486

125-
if (selectedNamespace) {
126-
fetchDatasets(selectedNamespace, PAGE_SIZE * directionPage)
127-
setState({ ...state, page: directionPage })
128-
}
87+
fetchDatasets(selectedNamespace || '', PAGE_SIZE, directionPage * PAGE_SIZE)
88+
// reset page scroll
89+
window.scrollTo(0, 0)
90+
setState({ ...state, page: directionPage })
12991
}
13092

13193
const i18next = require('i18next')
@@ -141,36 +103,15 @@ const Datasets: React.FC<DatasetsProps> = ({
141103
</Box>
142104
) : (
143105
<>
144-
<Box display={'flex'} justifyContent={'space-between'} p={2}>
145-
<Box>
146-
<MqText heading>{i18next.t('datasets_route.heading')}</MqText>
147-
Page: {pageNavigation()}
148-
</Box>
149-
<Box>
150-
<Tooltip title={i18next.t('events_route.previous_page')}>
151-
<IconButton
152-
sx={{
153-
marginLeft: theme.spacing(2),
154-
}}
155-
color='primary'
156-
disabled={state.page === 1}
157-
onClick={() => handleClickPage('prev')}
158-
size='large'
159-
>
160-
<ChevronLeftRounded />
161-
</IconButton>
162-
</Tooltip>
163-
<Tooltip title={i18next.t('events_route.next_page')}>
164-
<IconButton
165-
color='primary'
166-
disabled={state.pageIsLast}
167-
onClick={() => handleClickPage('next')}
168-
size='large'
169-
>
170-
<ChevronRightRounded />
171-
</IconButton>
172-
</Tooltip>
173-
</Box>
106+
<Box p={2} display={'flex'}>
107+
<MqText heading>{i18next.t('datasets_route.heading')}</MqText>
108+
<Chip
109+
size={'small'}
110+
variant={'outlined'}
111+
color={'primary'}
112+
sx={{ marginLeft: 1 }}
113+
label={totalCount + ' total'}
114+
></Chip>
174115
</Box>
175116
<Table size='small'>
176117
<TableHead>
@@ -233,6 +174,41 @@ const Datasets: React.FC<DatasetsProps> = ({
233174
})}
234175
</TableBody>
235176
</Table>
177+
<Box display={'flex'} justifyContent={'flex-end'} alignItems={'center'} mb={2}>
178+
<MqText subdued>
179+
<>
180+
{PAGE_SIZE * state.page + 1} -{' '}
181+
{Math.min(PAGE_SIZE * (state.page + 1), totalCount)} of {totalCount}
182+
</>
183+
</MqText>
184+
<Tooltip title={i18next.t('events_route.previous_page')}>
185+
<span>
186+
<IconButton
187+
sx={{
188+
marginLeft: theme.spacing(2),
189+
}}
190+
color='primary'
191+
disabled={state.page === 0}
192+
onClick={() => handleClickPage('prev')}
193+
size='large'
194+
>
195+
<ChevronLeftRounded />
196+
</IconButton>
197+
</span>
198+
</Tooltip>
199+
<Tooltip title={i18next.t('events_route.next_page')}>
200+
<span>
201+
<IconButton
202+
color='primary'
203+
onClick={() => handleClickPage('next')}
204+
size='large'
205+
disabled={state.page === Math.ceil(totalCount / PAGE_SIZE) - 1}
206+
>
207+
<ChevronRightRounded />
208+
</IconButton>
209+
</span>
210+
</Tooltip>
211+
</Box>
236212
</>
237213
)}
238214
</>
@@ -243,6 +219,7 @@ const Datasets: React.FC<DatasetsProps> = ({
243219

244220
const mapStateToProps = (state: IState) => ({
245221
datasets: state.datasets.result,
222+
totalCount: state.datasets.totalCount,
246223
isDatasetsLoading: state.datasets.isLoading,
247224
isDatasetsInit: state.datasets.init,
248225
selectedNamespace: state.namespaces.selectedNamespace,

web/src/routes/events/Events.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ import { useTheme } from '@emotion/react'
3030
import Box from '@mui/material/Box'
3131
import CircularProgress from '@mui/material/CircularProgress/CircularProgress'
3232
import IconButton from '@mui/material/IconButton'
33+
import MqCopy from '../../components/core/copy/MqCopy'
3334
import MqDatePicker from '../../components/core/date-picker/MqDatePicker'
3435
import MqEmpty from '../../components/core/empty/MqEmpty'
3536
import MqJsonView from '../../components/core/json-view/MqJsonView'
3637
import MqStatus from '../../components/core/status/MqStatus'
3738
import MqText from '../../components/core/text/MqText'
3839
import React, { useEffect, useRef } from 'react'
3940
import moment from 'moment'
40-
import MqCopy from '../../components/core/copy/MqCopy'
4141

4242
interface StateProps {
4343
events: Event[]
@@ -143,6 +143,8 @@ const Events: React.FC<EventsProps> = ({
143143
PAGE_SIZE,
144144
directionPage * PAGE_SIZE
145145
)
146+
// reset page scroll
147+
window.scrollTo(0, 0)
146148
setState({ ...state, page: directionPage, rowExpanded: null })
147149
}
148150

@@ -249,9 +251,9 @@ const Events: React.FC<EventsProps> = ({
249251
}}
250252
>
251253
<TableCell align='left'>
252-
<Box display={"flex"} alignItems={"center"}>
254+
<Box display={'flex'} alignItems={'center'}>
253255
<MqText font={'mono'}>{event.run.runId}</MqText>
254-
<MqCopy string={event.run.runId}/>
256+
<MqCopy string={event.run.runId} />
255257
</Box>
256258
</TableCell>
257259
<TableCell align='left'>

0 commit comments

Comments
 (0)