Skip to content

Commit

Permalink
chore: fixed period select refactor (#2989)
Browse files Browse the repository at this point in the history
Co-authored-by: Jen Jones Arnesen <[email protected]>
  • Loading branch information
turban and jenniferarnesen authored Sep 29, 2023
1 parent 7f84e89 commit 3decb2b
Show file tree
Hide file tree
Showing 4 changed files with 469 additions and 122 deletions.
2 changes: 1 addition & 1 deletion src/components/orgunits/OrgUnitData.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const ORGUNIT_PROFILE_QUERY = {
// Only YEARLY period type is supported in first version
const periodType = 'YEARLY'
const currentYear = String(new Date().getFullYear())
const periods = getFixedPeriodsByType(periodType, currentYear)
const periods = getFixedPeriodsByType({ periodType, currentYear })
const defaultPeriod = filterFuturePeriods(periods)[0] || periods[0]

/*
Expand Down
226 changes: 115 additions & 111 deletions src/components/periods/PeriodSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
} from '@dhis2/ui'
import cx from 'classnames'
import PropTypes from 'prop-types'
import React, { Component } from 'react'
import React, { useState, useMemo, useCallback, useEffect } from 'react'
import usePrevious from '../../hooks/usePrevious.js'
import {
getFixedPeriodsByType,
filterFuturePeriods,
Expand All @@ -16,124 +17,127 @@ import { getYear } from '../../util/time.js'
import { SelectField } from '../core/index.js'
import styles from './styles/PeriodSelect.module.css'

class PeriodSelect extends Component {
static propTypes = {
onChange: PropTypes.func.isRequired,
className: PropTypes.string,
errorText: PropTypes.string,
period: PropTypes.shape({
id: PropTypes.string.isRequired,
startDate: PropTypes.string,
}),
periodType: PropTypes.string,
}

state = {
year: null,
periods: null,
}

componentDidMount() {
this.setPeriods()
}

componentDidUpdate(prevProps, prevState) {
const { periodType, period, onChange } = this.props
const { year, periods } = this.state

if (periodType !== prevProps.periodType) {
this.setPeriods()
} else if (periods && !period) {
onChange(filterFuturePeriods(periods)[0] || periods[0]) // Autoselect most recent period
}

// Change period if year is changed (but keep period index)
if (period && prevState.periods && year !== prevState.year) {
const periodIndex = prevState.periods.findIndex(
(item) => item.id === period.id
)
onChange(periods[periodIndex])
}
}

render() {
const { periodType, period, onChange, className, errorText } =
this.props
const { periods } = this.state

if (!periods) {
return null
const PeriodSelect = ({
onChange,
className,
errorText,
firstDate,
lastDate,
period,
periodType,
}) => {
const [year, setYear] = useState(getYear(period?.startDate || lastDate))
const prevYear = usePrevious(year)

// Set periods when periodType or year changes
/* eslint-disable react-hooks/exhaustive-deps */
const periods = useMemo(
() =>
periodType
? getFixedPeriodsByType({
periodType,
year,
firstDate,
lastDate,
})
: [period], // saved map period (not included in depency array by design)
[periodType, year, firstDate, lastDate]
)
/* eslint-enable react-hooks/exhaustive-deps */

const periodIndex = useMemo(
() => period && periods.findIndex((p) => p.id === period.id),
[period, periods]
)

const prevPeriodIndex = usePrevious(periodIndex)

// Increment/decrement year
const changeYear = useCallback(
(change) => {
const newYear = year + change

if (
(!firstDate || newYear >= getYear(firstDate)) &&
(!lastDate || newYear <= getYear(lastDate))
) {
setYear(newYear)
}
},
[year, firstDate, lastDate]
)

// Autoselect most recent period
useEffect(() => {
if (!period && periods) {
onChange(filterFuturePeriods(periods)[0] || periods[0])
}
}, [period, periods, onChange])

const value =
period && periods.some((p) => p.id === period.id) ? period.id : null

return (
<div className={cx(styles.periodSelect, className)}>
<SelectField
label={i18n.t('Period')}
items={periods}
value={value}
onChange={onChange}
errorText={!value && errorText ? errorText : null}
className={styles.select}
dataTest="year-select"
/>
{periodType && (
<div className={styles.stepper}>
<Tooltip content={i18n.t('Previous year')}>
<Button
secondary
icon={<IconChevronLeft24 />}
onClick={this.previousYear}
dataTest="button-previous-year"
/>
</Tooltip>
<Tooltip content={i18n.t('Next year')}>
<Button
secondary
icon={<IconChevronRight24 />}
onClick={this.nextYear}
dataTest="button-next-year"
/>
</Tooltip>
</div>
)}
</div>
)
}

setPeriods() {
const { periodType, period } = this.props
const year = this.state.year || getYear(period && period.startDate)
let periods
// Keep the same period position when year changes
useEffect(() => {
if (year !== prevYear && prevPeriodIndex >= 0) {
const newPeriod = periods[prevPeriodIndex]

if (periodType) {
periods = getFixedPeriodsByType(periodType, year)
} else if (period) {
periods = [period] // If period is loaded in favorite
if (newPeriod) {
onChange(newPeriod)
}
}
}, [year, prevYear, periods, prevPeriodIndex, onChange])

this.setState({ periods, year })
if (!periods) {
return null
}

nextYear = () => {
this.changeYear(1)
}

previousYear = () => {
this.changeYear(-1)
}

changeYear = (change) => {
const { periodType } = this.props
const year = this.state.year + change
const value =
period && periods.some((p) => p.id === period.id) ? period.id : null

return (
<div className={cx(styles.periodSelect, className)}>
<SelectField
label={i18n.t('Period')}
items={periods}
value={value}
onChange={onChange}
errorText={!value && errorText ? errorText : null}
className={styles.select}
dataTest="year-select"
/>
{periodType && (
<div className={styles.stepper}>
<Tooltip content={i18n.t('Previous year')}>
<Button
secondary
icon={<IconChevronLeft24 />}
onClick={() => changeYear(-1)}
dataTest="button-previous-year"
/>
</Tooltip>
<Tooltip content={i18n.t('Next year')}>
<Button
secondary
icon={<IconChevronRight24 />}
onClick={() => changeYear(1)}
dataTest="button-next-year"
/>
</Tooltip>
</div>
)}
</div>
)
}

this.setState({
year,
periods: getFixedPeriodsByType(periodType, year),
})
}
PeriodSelect.propTypes = {
onChange: PropTypes.func.isRequired,
className: PropTypes.string,
errorText: PropTypes.string,
firstDate: PropTypes.string,
lastDate: PropTypes.string,
period: PropTypes.shape({
id: PropTypes.string.isRequired,
startDate: PropTypes.string,
}),
periodType: PropTypes.string,
}

export default PeriodSelect
Loading

0 comments on commit 3decb2b

Please sign in to comment.