Skip to content

Commit

Permalink
Paging on jobs and datasets (#2614)
Browse files Browse the repository at this point in the history
* Jobs paging.

* Datasets paging.

* Fixing tests.

---------

Co-authored-by: phix <peter.hicks@astronomer.io>
  • Loading branch information
phixMe and phix authored Sep 14, 2023
1 parent d1d47e2 commit d0d35fa
Show file tree
Hide file tree
Showing 12 changed files with 202 additions and 129 deletions.
8 changes: 5 additions & 3 deletions web/src/__tests__/reducers/datasets.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

import * as actionTypes from '../../store/actionCreators/actionTypes'
import datasetsReducer, { initialState } from '../../store/reducers/datasets'
import datasetsReducer, {IDatasetsAction, initialState} from '../../store/reducers/datasets'

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

Expand All @@ -11,13 +11,15 @@ describe('datasets reducer', () => {
const action = {
type: actionTypes.FETCH_DATASETS_SUCCESS,
payload: {
datasets: datasets
datasets: datasets,
totalCount: 16
}
}
} as IDatasetsAction
expect(datasetsReducer(initialState, action)).toStrictEqual({
init: true,
isLoading: false,
result: datasets,
totalCount: 16,
deletedDatasetName: ''
})
})
Expand Down
10 changes: 6 additions & 4 deletions web/src/__tests__/reducers/jobs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
// SPDX-License-Identifier: Apache-2.0

import * as actionTypes from '../../store/actionCreators/actionTypes'
import jobsReducer, { initialState } from '../../store/reducers/jobs'
import jobsReducer, {IJobsAction, initialState} from '../../store/reducers/jobs'
import { stopWatchDuration } from "../../helpers/time";
import { Job } from "../../types/api";

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

Expand All @@ -13,10 +14,11 @@ describe('jobs reducer', () => {
const action = {
type: actionTypes.FETCH_JOBS_SUCCESS,
payload: {
jobs: jobs
totalCount: 13,
jobs: jobs as Job[]
}
}
expect(jobsReducer(initialState, action)).toStrictEqual({ isLoading: false, result: jobs, init: true, deletedJobName: '' })
} as IJobsAction
expect(jobsReducer(initialState, action)).toStrictEqual({ isLoading: false, result: jobs, totalCount: 13, init: true, deletedJobName: '' })
})
})

Expand Down
139 changes: 58 additions & 81 deletions web/src/routes/datasets/Datasets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import * as Redux from 'redux'
import { ChevronLeftRounded, ChevronRightRounded } from '@mui/icons-material'
import {
Chip,
Container,
Table,
TableBody,
Expand Down Expand Up @@ -35,12 +36,11 @@ interface StateProps {
isDatasetsLoading: boolean
isDatasetsInit: boolean
selectedNamespace: Nullable<string>
totalCount: number
}

interface DatasetsState {
datasets: Dataset[]
page: number
pageIsLast: boolean
}

interface DispatchProps {
Expand All @@ -50,52 +50,29 @@ interface DispatchProps {

type DatasetsProps = StateProps & DispatchProps

const PAGE_SIZE = 20

const Datasets: React.FC<DatasetsProps> = ({
datasets,
totalCount,
isDatasetsLoading,
isDatasetsInit,
selectedNamespace,
fetchDatasets,
resetDatasets,
}) => {
const PAGE_SIZE = 20
const mounted = React.useRef<boolean>(false)
const prevSelectedNamespace = React.useRef<Nullable<string>>()

const defaultState = {
datasets: [],
page: 1,
pageIsLast: false,
page: 0,
}
const [state, setState] = React.useState<DatasetsState>(defaultState)

const theme = createTheme(useTheme())

React.useEffect(() => {
if (!mounted.current) {
// on mount
if (selectedNamespace) {
fetchDatasets(selectedNamespace, PAGE_SIZE)
}
mounted.current = true
} else {
// on update
if (prevSelectedNamespace.current !== selectedNamespace && selectedNamespace) {
fetchDatasets(selectedNamespace, PAGE_SIZE)
setState(defaultState)
}

if (datasets !== state.datasets) {
setState({
...state,
datasets,
pageIsLast: datasets.length < state.page * PAGE_SIZE,
})
}

prevSelectedNamespace.current = selectedNamespace
if (selectedNamespace) {
fetchDatasets(selectedNamespace, PAGE_SIZE, state.page * PAGE_SIZE)
}
})
}, [selectedNamespace, state.page])

React.useEffect(() => {
return () => {
Expand All @@ -104,28 +81,13 @@ const Datasets: React.FC<DatasetsProps> = ({
}
}, [])

const pageNavigation = () => {
const { datasets, page, pageIsLast } = 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})`
}

const handleClickPage = (direction: 'prev' | 'next') => {
const directionPage = direction === 'next' ? state.page + 1 : state.page - 1

if (selectedNamespace) {
fetchDatasets(selectedNamespace, PAGE_SIZE * directionPage)
setState({ ...state, page: directionPage })
}
fetchDatasets(selectedNamespace || '', PAGE_SIZE, directionPage * PAGE_SIZE)
// reset page scroll
window.scrollTo(0, 0)
setState({ ...state, page: directionPage })
}

const i18next = require('i18next')
Expand All @@ -141,36 +103,15 @@ const Datasets: React.FC<DatasetsProps> = ({
</Box>
) : (
<>
<Box display={'flex'} justifyContent={'space-between'} p={2}>
<Box>
<MqText heading>{i18next.t('datasets_route.heading')}</MqText>
Page: {pageNavigation()}
</Box>
<Box>
<Tooltip title={i18next.t('events_route.previous_page')}>
<IconButton
sx={{
marginLeft: theme.spacing(2),
}}
color='primary'
disabled={state.page === 1}
onClick={() => handleClickPage('prev')}
size='large'
>
<ChevronLeftRounded />
</IconButton>
</Tooltip>
<Tooltip title={i18next.t('events_route.next_page')}>
<IconButton
color='primary'
disabled={state.pageIsLast}
onClick={() => handleClickPage('next')}
size='large'
>
<ChevronRightRounded />
</IconButton>
</Tooltip>
</Box>
<Box p={2} display={'flex'}>
<MqText heading>{i18next.t('datasets_route.heading')}</MqText>
<Chip
size={'small'}
variant={'outlined'}
color={'primary'}
sx={{ marginLeft: 1 }}
label={totalCount + ' total'}
></Chip>
</Box>
<Table size='small'>
<TableHead>
Expand Down Expand Up @@ -233,6 +174,41 @@ const Datasets: React.FC<DatasetsProps> = ({
})}
</TableBody>
</Table>
<Box display={'flex'} justifyContent={'flex-end'} alignItems={'center'} mb={2}>
<MqText subdued>
<>
{PAGE_SIZE * state.page + 1} -{' '}
{Math.min(PAGE_SIZE * (state.page + 1), totalCount)} of {totalCount}
</>
</MqText>
<Tooltip title={i18next.t('events_route.previous_page')}>
<span>
<IconButton
sx={{
marginLeft: theme.spacing(2),
}}
color='primary'
disabled={state.page === 0}
onClick={() => handleClickPage('prev')}
size='large'
>
<ChevronLeftRounded />
</IconButton>
</span>
</Tooltip>
<Tooltip title={i18next.t('events_route.next_page')}>
<span>
<IconButton
color='primary'
onClick={() => handleClickPage('next')}
size='large'
disabled={state.page === Math.ceil(totalCount / PAGE_SIZE) - 1}
>
<ChevronRightRounded />
</IconButton>
</span>
</Tooltip>
</Box>
</>
)}
</>
Expand All @@ -243,6 +219,7 @@ const Datasets: React.FC<DatasetsProps> = ({

const mapStateToProps = (state: IState) => ({
datasets: state.datasets.result,
totalCount: state.datasets.totalCount,
isDatasetsLoading: state.datasets.isLoading,
isDatasetsInit: state.datasets.init,
selectedNamespace: state.namespaces.selectedNamespace,
Expand Down
8 changes: 5 additions & 3 deletions web/src/routes/events/Events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ import { useTheme } from '@emotion/react'
import Box from '@mui/material/Box'
import CircularProgress from '@mui/material/CircularProgress/CircularProgress'
import IconButton from '@mui/material/IconButton'
import MqCopy from '../../components/core/copy/MqCopy'
import MqDatePicker from '../../components/core/date-picker/MqDatePicker'
import MqEmpty from '../../components/core/empty/MqEmpty'
import MqJsonView from '../../components/core/json-view/MqJsonView'
import MqStatus from '../../components/core/status/MqStatus'
import MqText from '../../components/core/text/MqText'
import React, { useEffect, useRef } from 'react'
import moment from 'moment'
import MqCopy from '../../components/core/copy/MqCopy'

interface StateProps {
events: Event[]
Expand Down Expand Up @@ -143,6 +143,8 @@ const Events: React.FC<EventsProps> = ({
PAGE_SIZE,
directionPage * PAGE_SIZE
)
// reset page scroll
window.scrollTo(0, 0)
setState({ ...state, page: directionPage, rowExpanded: null })
}

Expand Down Expand Up @@ -249,9 +251,9 @@ const Events: React.FC<EventsProps> = ({
}}
>
<TableCell align='left'>
<Box display={"flex"} alignItems={"center"}>
<Box display={'flex'} alignItems={'center'}>
<MqText font={'mono'}>{event.run.runId}</MqText>
<MqCopy string={event.run.runId}/>
<MqCopy string={event.run.runId} />
</Box>
</TableCell>
<TableCell align='left'>
Expand Down
Loading

0 comments on commit d0d35fa

Please sign in to comment.