Skip to content

Commit

Permalink
feat: add support for period item max limit (DHIS2-16399)
Browse files Browse the repository at this point in the history
  • Loading branch information
edoardo committed Jan 12, 2024
1 parent 26f4fa8 commit 9f39c2e
Show file tree
Hide file tree
Showing 11 changed files with 121 additions and 52 deletions.
10 changes: 5 additions & 5 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2023-12-18T09:34:16.389Z\n"
"PO-Revision-Date: 2023-12-18T09:34:16.389Z\n"
"POT-Creation-Date: 2024-01-03T12:42:34.756Z\n"
"PO-Revision-Date: 2024-01-03T12:42:34.756Z\n"

msgid "view only"
msgstr "view only"
Expand Down Expand Up @@ -654,15 +654,15 @@ msgstr "Select year"
msgid "Period"
msgstr "Period"

msgid "Selected Periods"
msgstr "Selected Periods"

msgid "Relative periods"
msgstr "Relative periods"

msgid "Fixed periods"
msgstr "Fixed periods"

msgid "Selected Periods"
msgstr "Selected Periods"

msgid "No periods selected"
msgstr "No periods selected"

Expand Down
5 changes: 4 additions & 1 deletion src/components/PeriodDimension/PeriodDimension.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const PeriodDimension = ({
selectedPeriods,
rightFooter,
excludedPeriodTypes,
infoBoxMessage,
}) => {
const { systemInfo } = useConfig()
const result = useDataQuery(userSettingsQuery)
Expand All @@ -36,7 +37,8 @@ const PeriodDimension = ({
return (
<PeriodTransfer
onSelect={selectPeriods}
initialSelectedPeriods={selectedPeriods}
selectedItems={selectedPeriods}
infoBoxMessage={infoBoxMessage}
rightFooter={rightFooter}
dataTest={'period-dimension'}
excludedPeriodTypes={excludedPeriodTypes}
Expand All @@ -48,6 +50,7 @@ const PeriodDimension = ({
PeriodDimension.propTypes = {
onSelect: PropTypes.func.isRequired,
excludedPeriodTypes: PropTypes.arrayOf(PropTypes.string),
infoBoxMessage: PropTypes.string,
rightFooter: PropTypes.node,
selectedPeriods: PropTypes.array,
}
Expand Down
88 changes: 55 additions & 33 deletions src/components/PeriodDimension/PeriodTransfer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getNowInCalendar } from '@dhis2/multi-calendar-dates'
import { TabBar, Tab, Transfer } from '@dhis2/ui'
import { IconInfo16, TabBar, Tab, Transfer } from '@dhis2/ui'
import PropTypes from 'prop-types'
import React, { useState } from 'react'
import PeriodIcon from '../../assets/DimensionItemIcons/PeriodIcon.js' //TODO: Reimplement the icon.js
Expand All @@ -17,13 +17,33 @@ import { getFixedPeriodsOptionsById } from './utils/fixedPeriods.js'
import { MONTHLY, QUARTERLY } from './utils/index.js'
import { getRelativePeriodsOptionsById } from './utils/relativePeriods.js'

const RightHeader = ({ infoText }) => (
<>
<p className="rightHeader">{i18n.t('Selected Periods')}</p>
{infoText && (
<div className="info-container">
<div>
<IconInfo16 />
</div>
<span className="info-text">{infoText}</span>
</div>
)}
<style jsx>{styles}</style>
</>
)

RightHeader.propTypes = {
infoText: PropTypes.string,
}

const PeriodTransfer = ({
onSelect,
dataTest,
initialSelectedPeriods,
selectedItems,
rightFooter,
excludedPeriodTypes,
periodsSettings,
infoBoxMessage,
}) => {
const defaultRelativePeriodType = excludedPeriodTypes.includes(MONTHLY)
? getRelativePeriodsOptionsById(QUARTERLY)
Expand All @@ -46,9 +66,6 @@ const PeriodTransfer = ({
const [allPeriods, setAllPeriods] = useState(
defaultRelativePeriodType.getPeriods()
)
const [selectedPeriods, setSelectedPeriods] = useState(
initialSelectedPeriods
)
const [isRelative, setIsRelative] = useState(true)
const [relativeFilter, setRelativeFilter] = useState({
periodType: defaultRelativePeriodType.id,
Expand All @@ -58,6 +75,11 @@ const PeriodTransfer = ({
year: defaultFixedPeriodYear.toString(),
})

const isActive = (value) => {
const item = selectedItems.find((item) => item.id === value)
return !item || item.isActive
}

const onIsRelativeClick = (state) => {
if (state !== isRelative) {
setIsRelative(state)
Expand Down Expand Up @@ -132,13 +154,6 @@ const PeriodTransfer = ({
</>
)

const renderRightHeader = () => (
<>
<p className="rightHeader">{i18n.t('Selected Periods')}</p>
<style jsx>{styles}</style>
</>
)

const onSelectFixedPeriods = (filter) => {
setFixedFilter(filter)
setAllPeriods(
Expand All @@ -162,35 +177,40 @@ const PeriodTransfer = ({
return (
<Transfer
onChange={({ selected }) => {
const formattedItems = selected.map((id) => ({
id,
name: [...allPeriods, ...selectedPeriods].find(
const formattedItems = selected.map((id) => {
const matchingItem = [...allPeriods, ...selectedItems].find(
(item) => item.id === id
).name,
}))
setSelectedPeriods(formattedItems)
)

return {
id,
name: matchingItem.name,
isActive: matchingItem.isActive,
}
})
onSelect(formattedItems)
}}
selected={selectedPeriods.map((period) => period.id)}
selected={selectedItems.map((period) => period.id)}
leftHeader={renderLeftHeader()}
enableOrderChange
height={TRANSFER_HEIGHT}
optionsWidth={TRANSFER_OPTIONS_WIDTH}
selectedWidth={TRANSFER_SELECTED_WIDTH}
selectedEmptyComponent={renderEmptySelection()}
rightHeader={renderRightHeader()}
rightHeader={<RightHeader infoText={infoBoxMessage} />}
rightFooter={rightFooter}
options={[...allPeriods, ...selectedPeriods].map(
({ id, name }) => ({
label: name,
value: id,
})
)}
options={[...allPeriods, ...selectedItems].map(({ id, name }) => ({
label: name,
value: id,
}))}
renderOption={(props) => (
<TransferOption
/* eslint-disable react/prop-types */
{...props}
active={isActive(props.value)}
icon={PeriodIcon}
dataTest={`${dataTest}-transfer-option`}
/* eslint-enable react/prop-types */
/>
)}
dataTest={`${dataTest}-transfer`}
Expand All @@ -199,7 +219,7 @@ const PeriodTransfer = ({
}

PeriodTransfer.defaultProps = {
initialSelectedPeriods: [],
selectedItems: [],
excludedPeriodTypes: [],
periodsSettings: {
calendar: 'gregory',
Expand All @@ -211,17 +231,19 @@ PeriodTransfer.propTypes = {
onSelect: PropTypes.func.isRequired,
dataTest: PropTypes.string,
excludedPeriodTypes: PropTypes.arrayOf(PropTypes.string),
initialSelectedPeriods: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string,
name: PropTypes.string,
})
),
infoBoxMessage: PropTypes.string,
periodsSettings: PropTypes.shape({
calendar: PropTypes.string,
locale: PropTypes.string,
}),
rightFooter: PropTypes.node,
selectedItems: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string,
isActive: PropTypes.bool,
name: PropTypes.string,
})
),
}

export default PeriodTransfer
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ exports[`The Period Dimension component matches the snapshot 1`] = `
<PeriodTransfer
dataTest="period-dimension"
excludedPeriodTypes={Array []}
initialSelectedPeriods={Array []}
onSelect={[Function]}
periodsSettings={
Object {
Expand All @@ -13,5 +12,6 @@ exports[`The Period Dimension component matches the snapshot 1`] = `
}
}
rightFooter={<React.Fragment />}
selectedItems={Array []}
/>
`;
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,7 @@ exports[`The Period Selector component matches the snapshot 1`] = `
optionsWidth="420px"
renderOption={[Function]}
rightFooter={<React.Fragment />}
rightHeader={
<React.Fragment>
<p
className="rightHeader"
>
Selected Periods
</p>
<style>
</style>
</React.Fragment>
}
rightHeader={<RightHeader />}
selected={Array []}
selectedEmptyComponent={
<React.Fragment>
Expand Down
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,12 @@ export {
export {
getAvailableAxes,
getDisallowedDimensions,
getDimensionMaxNumberOfItems,
getAxisMaxNumberOfItems,
getAxisMaxNumberOfDimensions,
getAxisMinNumberOfDimensions,
hasAxisTooManyItems,
hasDimensionTooManyItems,
getAxisPerLockedDimension,
getAllLockedDimensionIds,
canDimensionBeAddedToAxis,
Expand Down
21 changes: 21 additions & 0 deletions src/modules/layoutUiRules/__tests__/rules.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ const allArrayItemsAreValid = (allItems, validItems) =>
const allArrayItemsAreValidAxisIds = (array) =>
allArrayItemsAreValid(array, ALL_AXIS_IDS)

const allArrayItemsAreValidDimensionIds = (array) =>
allArrayItemsAreValid(array, lockableDims)

const onlyRulesWithProp = (ruleProp) =>
testResourceRules.filter((rule) => rule[ruleProp])

Expand All @@ -49,6 +52,15 @@ const testPropIsArray = (ruleProp) =>
).toBe(true)
})

const testKeysAreValidDimensionIds = (ruleProp) =>
it('keys should be valid dimension ids', () => {
expect(
onlyRulesWithProp(ruleProp).every((rule) =>
allArrayItemsAreValidDimensionIds(Object.keys(rule[ruleProp]))
)
).toBe(true)
})

const testKeysAreValidAxisIds = (ruleProp) =>
it('keys should be valid axis ids', () => {
expect(
Expand Down Expand Up @@ -127,6 +139,15 @@ describe("verify each rule's ", () => {
})
})

describe('MAX_ITEMS_PER_DIMENSION', () => {
const ruleProp = testResourceAllRuleProps['MAX_ITEMS_PER_DIMENSION']

testPropHasKeysAndValues(ruleProp)
testKeysAreValidDimensionIds(ruleProp)
testNoValuesZero(ruleProp)
testNoValuesNegative(ruleProp)
})

describe('AVAILABLE_AXES', () => {
const ruleProp = testResourceAllRuleProps['AVAILABLE_AXES']

Expand Down
2 changes: 2 additions & 0 deletions src/modules/layoutUiRules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ export {
getAxisMinNumberOfDimsByVisType as getAxisMinNumberOfDimensions,
getAxisPerLockedDimByVisType as getAxisPerLockedDimension,
getAllLockedDimIdsByVisType as getAllLockedDimensionIds,
getDimensionMaxNumberOfItemsByVisType as getDimensionMaxNumberOfItems,
} from './rulesHelper.js'

export {
hasAxisTooManyItemsByVisType as hasAxisTooManyItems,
hasDimensionTooManyItemsByVisType as hasDimensionTooManyItems,
isDimensionLockedByVisType as isDimensionLocked,
isAxisFullByVisType as isAxisFull,
canDimensionBeAddedToAxisByVisType as canDimensionBeAddedToAxis,
Expand Down
10 changes: 10 additions & 0 deletions src/modules/layoutUiRules/rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
DIMENSION_ID_PERIOD,
DIMENSION_ID_DATA,
DIMENSION_ID_ORGUNIT,
DIMENSION_ID_ASSIGNED_CATEGORIES,
} from '../predefinedDimensions.js'
import {
VIS_TYPE_COLUMN,
Expand All @@ -32,6 +33,7 @@ const RULE_PROP_AVAILABLE_AXES = 'availableAxes',
RULE_PROP_MAX_DIMS_PER_AXIS = 'maxNumberOfDimsPerAxis',
RULE_PROP_MIN_DIMS_PER_AXIS = 'minNumberOfDimsPerAxis',
RULE_PROP_MAX_ITEMS_PER_AXIS = 'maxNumberOfItemsPerAxis',
RULE_PROP_MAX_ITEMS_PER_DIMENSION = 'maxNumberOfItemsPerDimension',
RULE_PROP_DISALLOWED_DIMS = 'disallowedDims',
RULE_PROP_LOCKED_DIMS = 'lockedDims'

Expand Down Expand Up @@ -135,11 +137,15 @@ const outliersTableRules = {
[RULE_PROP_MIN_DIMS_PER_AXIS]: {
[AXIS_ID_COLUMNS]: 3,
},
[RULE_PROP_MAX_ITEMS_PER_DIMENSION]: {
[DIMENSION_ID_PERIOD]: 1,
},
[RULE_PROP_LOCKED_DIMS]: {
[DIMENSION_ID_DATA]: AXIS_ID_COLUMNS,
[DIMENSION_ID_PERIOD]: AXIS_ID_COLUMNS,
[DIMENSION_ID_ORGUNIT]: AXIS_ID_COLUMNS,
},
[RULE_PROP_DISALLOWED_DIMS]: [DIMENSION_ID_ASSIGNED_CATEGORIES],
}

const visTypeToRules = {
Expand Down Expand Up @@ -183,6 +189,9 @@ export const getMaxNumberOfDimsPerAxisByVisType = (visType) =>
export const getMinNumberOfDimsPerAxisByVisType = (visType) =>
getRulesByVisType(visType)[RULE_PROP_MIN_DIMS_PER_AXIS] || {}

export const getMaxNumberOfItemsPerDimensionByVisType = (visType) =>
getRulesByVisType(visType)[RULE_PROP_MAX_ITEMS_PER_DIMENSION] || {}

export const getMaxNumberOfItemsPerAxisByVisType = (visType) =>
getRulesByVisType(visType)[RULE_PROP_MAX_ITEMS_PER_AXIS] || {}

Expand All @@ -203,6 +212,7 @@ export const testResourceAllRuleProps = {
MAX_DIMS_PER_AXIS: RULE_PROP_MAX_DIMS_PER_AXIS,
MIN_DIMS_PER_AXIS: RULE_PROP_MIN_DIMS_PER_AXIS,
MAX_ITEMS_PER_AXIS: RULE_PROP_MAX_ITEMS_PER_AXIS,
MAX_ITEMS_PER_DIMENSION: RULE_PROP_MAX_ITEMS_PER_DIMENSION,
DISALLOWED_DIMS: RULE_PROP_DISALLOWED_DIMS,
LOCKED_DIMS: RULE_PROP_LOCKED_DIMS,
}
4 changes: 4 additions & 0 deletions src/modules/layoutUiRules/rulesHelper.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
getMaxNumberOfItemsPerDimensionByVisType,
getMaxNumberOfItemsPerAxisByVisType,
getMaxNumberOfDimsPerAxisByVisType,
getMinNumberOfDimsPerAxisByVisType,
Expand Down Expand Up @@ -26,3 +27,6 @@ export const getAxisPerLockedDimByVisType = (visType, dimensionId) =>

export const getAllLockedDimIdsByVisType = (visType) =>
Object.keys(getLockedDimsByVisType(visType))

export const getDimensionMaxNumberOfItemsByVisType = (visType, dimensionId) =>
getMaxNumberOfItemsPerDimensionByVisType(visType)[dimensionId]
Loading

0 comments on commit 9f39c2e

Please sign in to comment.