Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: fixed period select refactor #2989

Merged
merged 6 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading