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

feat: [DHIS2-15463] Use dhis2 ui calendarInput component in working list #3712

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,9 @@ Then('the list should display data ordered descendingly by report date', () => {
.click();

cy.get('input[placeholder="From"]')
.type(`${lastYear}-01-01`);
.type(`${lastYear}-01-01`).blur();

cy.get('input[placeholder="To"]').click();
cy.get('input[placeholder="To"]').click().blur();

cy.contains('Update')
.click({ force: true });
Expand Down Expand Up @@ -399,10 +399,10 @@ When('you set the date of admission filter', () => {
cy.get('input[type="text"]')
.then(($elements) => {
cy.wrap($elements[0])
.type('2018-01-01');
.type('2018-01-01').blur();

cy.wrap($elements[1])
.type('2018-12-31');
.type('2018-12-31').blur();
});

cy.contains('Update')
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@dhis2/d2-ui-rich-text": "^7.4.0",
"@dhis2/d2-ui-sharing-dialog": "^7.3.3",
"@dhis2/ui": "^9.10.1",
"@dhis2-ui/calendar": "9.12.0-alpha.4",
"@joakim_sm/react-infinite-calendar": "^2.4.2",
"@material-ui/core": "3.9.4",
"@material-ui/icons": "3",
Expand Down Expand Up @@ -136,6 +137,7 @@
"@dhis2/app-runtime": "^3.10.2",
"@babel/preset-react": "7.16.7",
"@dhis2/ui": "^9.10.1",
"@dhis2-ui/calendar": "9.12.0-alpha.4",
"@js-temporal/polyfill": "0.4.3",
"core-js": "2.5.7",
"i18next": "^20.5.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,11 @@ import { isValidZeroOrPositiveInteger } from 'capture-core-utils/validators/form
import { SelectBoxes, orientations } from '../../FormFields/Options/SelectBoxes';
import { OptionSet } from '../../../metaData/OptionSet/OptionSet';
import { Option } from '../../../metaData/OptionSet/Option';

import type { UpdatableFilterContent } from '../types';
import { type DateValue } from './types';
import { FromDateFilter } from './From.component';
import { ToDateFilter } from './To.component';
import { isValidDate } from '../../../utils/validators/form';
import { parseDate } from '../../../utils/converters/date';
import { dataElementTypes } from '../../../metaData';
import type { UpdatableFilterContent } from '../types';
import './calendarFilterStyles.css';
import { mainOptionKeys, mainOptionTranslatedTexts } from './options';
import { getDateFilterData } from './dateFilterDataGetter';
Expand All @@ -29,7 +27,7 @@ const getStyles = (theme: Theme) => ({
width: 30,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
alignItems: 'start',
paddingTop: theme.typography.pxToRem(6),
fontSize: theme.typography.body1.fontSize,
},
Expand All @@ -43,18 +41,16 @@ const getStyles = (theme: Theme) => ({
});

export type Value = ?{
from?: ?string,
to?: ?string,
from?: ?DateValue,
to?: ?DateValue,
main?: ?string,
start?: ?string,
end?: ?string,
};

type Props = {
onCommitValue: (value: ?{ from?: ?string, to?: ?string }) => void,
onUpdate: (commitValue?: any) => void,
value: Value,
type: $Keys<typeof dataElementTypes>,
classes: {
fromToContainer: string,
inputContainer: string,
Expand All @@ -69,33 +65,33 @@ type State = {
submitAttempted: boolean,
};

const getAbsoluteRangeErrors = (fromValue, toValue, type, submitAttempted) => {
let errors = {
minValueError: null,
maxValueError: null,
dateLogicError: null,
};

if (!fromValue && !toValue) {
errors = {
...errors,
dateLogicError: submitAttempted ? i18n.t(DateFilter.errorMessages.ABSOLUTE_RANGE_WITHOUT_VALUES) : null,
};
} else {
const { isValid: isMinValueValid, error: minValueError } = DateFilter.validateField(fromValue, type);
const { isValid: isMaxValueValid, error: maxValueError } = DateFilter.validateField(toValue, type);
const hasDateLogicError = () =>
isMinValueValid && isMaxValueValid && fromValue && toValue && DateFilter.isFromAfterTo(fromValue, toValue);
// eslint-disable-next-line complexity
const getAbsoluteRangeErrors = (fromValue, toValue, submitAttempted) => {
const fromValueString = fromValue?.dateString;
const toValueString = toValue?.dateString;
const isFromValueValid = fromValue?.isValid;
const isToValueValid = toValue?.isValid;

errors = {
...errors,
minValueError,
maxValueError,
dateLogicError: hasDateLogicError() ? i18n.t(DateFilter.errorMessages.FROM_GREATER_THAN_TO) : null,
if (!fromValueString && !toValueString) {
return {
dateLogicError: submitAttempted
? i18n.t(DateFilter.errorMessages.ABSOLUTE_RANGE_WITHOUT_VALUES)
: null,
};
}

return errors;
const hasDateLogicError =
fromValueString &&
toValueString &&
isFromValueValid &&
isToValueValid &&
DateFilter.isFromAfterTo(fromValueString, toValueString);

return {
dateLogicError: hasDateLogicError
? i18n.t(DateFilter.errorMessages.FROM_GREATER_THAN_TO)
: null,
};
};

const getRelativeRangeErrors = (startValue, endValue, submitAttempted) => {
Expand All @@ -110,8 +106,8 @@ const getRelativeRangeErrors = (startValue, endValue, submitAttempted) => {
bufferLogicError: submitAttempted ? i18n.t(DateFilter.errorMessages.RELATIVE_RANGE_WITHOUT_VALUES) : null,
};
}
const { error: startValueError } = DateFilter.validateField(startValue, dataElementTypes.INTEGER_ZERO_OR_POSITIVE);
const { error: endValueError } = DateFilter.validateField(endValue, dataElementTypes.INTEGER_ZERO_OR_POSITIVE);
const { error: startValueError } = DateFilter.validateRelativeRangeValue(startValue);
const { error: endValueError } = DateFilter.validateRelativeRangeValue(endValue);
errors = {
...errors,
startValueError,
Expand All @@ -125,27 +121,20 @@ const isAbsoluteRangeFilterValid = (fromValue, toValue) => {
return false;
}

const parseResultFrom = fromValue ? parseDate(fromValue) : { isValid: true, moment: null };
const parseResultTo = toValue ? parseDate(toValue) : { isValid: true, moment: null };

if (!(parseResultFrom.isValid && parseResultTo.isValid)) {
if ((fromValue && !fromValue.isValid) || (toValue && !toValue.isValid)) {
return false;
}
const isValidMomentDate = () =>
parseResultFrom.momentDate &&
parseResultTo.momentDate &&
parseResultFrom.momentDate.isAfter(parseResultTo.momentDate);

return !isValidMomentDate();
return !DateFilter.isFromAfterTo(fromValue?.dateString, toValue?.dateString);
};

const isRelativeRangeFilterValid = (startValue, endValue) => {
if (!startValue && !endValue) {
return false;
}
if (
!DateFilter.validateField(startValue, dataElementTypes.INTEGER_ZERO_OR_POSITIVE).isValid ||
!DateFilter.validateField(endValue, dataElementTypes.INTEGER_ZERO_OR_POSITIVE).isValid
!DateFilter.validateRelativeRangeValue(startValue).isValid ||
!DateFilter.validateRelativeRangeValue(endValue).isValid
) {
return false;
}
Expand All @@ -154,29 +143,26 @@ const isRelativeRangeFilterValid = (startValue, endValue) => {

// $FlowFixMe[incompatible-variance] automated comment
class DateFilterPlain extends Component<Props, State> implements UpdatableFilterContent<Value> {
static validateField(value: ?string, type: $Keys<typeof dataElementTypes>) {
static validateRelativeRangeValue(value: ?string) {
if (!value) {
return {
isValid: true,
error: null,
};
}

// $FlowFixMe dataElementTypes flow error
const typeValidator = DateFilter.validatorForTypes[type];
const isValid = typeValidator(value);
const isValid = isValidZeroOrPositiveInteger(value);

return {
isValid,
// $FlowFixMe dataElementTypes flow error
error: isValid ? null : i18n.t(DateFilter.errorMessages[type]),
error: isValid ? null : i18n.t(DateFilter.errorMessages[dataElementTypes.INTEGER_ZERO_OR_POSITIVE]),
};
}

static isFilterValid(
mainValue?: ?string,
fromValue?: ?string,
toValue?: ?string,
fromValue?: ?DateValue,
toValue?: ?DateValue,
startValue?: ?string,
endValue?: ?string,
) {
Expand All @@ -191,32 +177,26 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
}

static isFromAfterTo(valueFrom: string, valueTo: string) {
const momentFrom = parseDate(valueFrom).momentDate;
const momentTo = parseDate(valueTo).momentDate;
// $FlowFixMe[incompatible-use] automated comment
// $FlowFixMe[incompatible-call] automated comment
return momentFrom.isAfter(momentTo);
const dateFrom = new Date(valueFrom);
const dateTo = new Date(valueTo);
return dateFrom > dateTo;
}

toD2DateTextFieldInstance: any;

constructor(props: Props) {
super(props);
this.state = { submitAttempted: false };
}

static errorMessages = {
ABSOLUTE_RANGE_WITHOUT_VALUES: 'Please specify a range',
RELATIVE_RANGE_WITHOUT_VALUES: 'Please specify the number of days',
FROM_GREATER_THAN_TO: "The From date can't be after the To date",
MIN_GREATER_THAN_MAX: 'Days in the past cannot be greater than days in the future',
[dataElementTypes.DATE]: 'Please provide a valid date',
[dataElementTypes.INTEGER_ZERO_OR_POSITIVE]: 'Please provide zero or a positive integer',
};

static validatorForTypes = {
[dataElementTypes.DATE]: isValidDate,
[dataElementTypes.INTEGER_ZERO_OR_POSITIVE]: isValidZeroOrPositiveInteger,
};

static mainOptionSet = new OptionSet('mainOptions', [
new Option((_this) => {
_this.text = mainOptionTranslatedTexts[mainOptionKeys.TODAY];
Expand Down Expand Up @@ -251,12 +231,14 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
_this.value = mainOptionKeys.ABSOLUTE_RANGE;
}),
]);

static optionSet = new OptionSet('mainOptions', [
new Option((_this) => {
_this.text = mainOptionTranslatedTexts[mainOptionKeys.RELATIVE_RANGE];
_this.value = mainOptionKeys.RELATIVE_RANGE;
}),
]);

onGetUpdateData(updatedValues?: Value) {
const value = typeof updatedValues !== 'undefined' ? updatedValues : this.props.value;

Expand Down Expand Up @@ -302,23 +284,11 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
}

handleEnterKeyInFrom = () => {
this.toD2DateTextFieldInstance.focus();
this.toD2DateTextFieldInstance && this.toD2DateTextFieldInstance.focus();
};

handleDateSelectedFromCalendarInFrom = () => {
this.toD2DateTextFieldInstance.focus();
};

handleEnterKeyInTo = (value: { [key: string]: string }) => {
// validate with updated values
const values = this.getUpdatedValue(value);
this.setState({ submitAttempted: true });

if (values && !DateFilter.isFilterValid(values.main, values.from, values.to, values.start, values.end)) {
this.props.onCommitValue(values);
} else {
this.props.onUpdate(values || null);
}
this.toD2DateTextFieldInstance && this.toD2DateTextFieldInstance.focus();
};

handleFieldBlur = (value: { [key: string]: string }) => {
Expand All @@ -342,7 +312,6 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
const toValue = values && values.to;
const startValue = values && values.start;
const endValue = values && values.end;
const type = this.props.type;
const errors = {
minValueError: null,
maxValueError: null,
Expand All @@ -353,7 +322,7 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
};

if (mainValue === mainOptionKeys.ABSOLUTE_RANGE) {
return { ...errors, ...getAbsoluteRangeErrors(fromValue, toValue, type, submitAttempted) };
return { ...errors, ...getAbsoluteRangeErrors(fromValue, toValue, submitAttempted) };
}

if (mainValue === mainOptionKeys.RELATIVE_RANGE) {
Expand All @@ -364,8 +333,9 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter

render() {
const { value, classes, onFocusUpdateButton } = this.props;
const { minValueError, maxValueError, startValueError, endValueError, dateLogicError, bufferLogicError } =
const { startValueError, endValueError, dateLogicError, bufferLogicError } =
this.getErrors();

return (
<div id="dateFilter">
<div>
Expand All @@ -384,9 +354,7 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
{/* $FlowSuppress: Flow not working 100% with HOCs */}
{/* $FlowFixMe[prop-missing] automated comment */}
<FromDateFilter
value={value && value.from}
error={minValueError}
errorClass={classes.error}
value={value && value.from && value.from.dateString}
onBlur={this.handleFieldBlur}
onEnterKey={this.handleEnterKeyInFrom}
onDateSelectedFromCalendar={this.handleDateSelectedFromCalendarInFrom}
Expand All @@ -397,11 +365,8 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
{/* $FlowSuppress: Flow not working 100% with HOCs */}
{/* $FlowFixMe[prop-missing] automated comment */}
<ToDateFilter
value={value && value.to}
error={maxValueError}
errorClass={classes.error}
value={value && value.to && value.to.dateString}
onBlur={this.handleFieldBlur}
onEnterKey={this.handleEnterKeyInTo}
textFieldRef={this.setToD2DateTextFieldInstance}
onFocusUpdateButton={onFocusUpdateButton}
/>
Expand All @@ -424,7 +389,6 @@ class DateFilterPlain extends Component<Props, State> implements UpdatableFilter
startValueError={startValueError}
endValueError={endValueError}
handleFieldBlur={this.handleFieldBlur}
handleEnterKeyInTo={this.handleEnterKeyInTo}
/>
</div>
<div className={classNames(classes.error, classes.logicErrorContainer)}>{dateLogicError}</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,14 @@ export class DateFilterManager extends React.Component<Props, State> {
static calculateAbsoluteRangeValueState(filter: DateFilterData) {
return {
main: mainOptionKeys.ABSOLUTE_RANGE,
from: filter.ge && DateFilterManager.convertDateForEdit(filter.ge),
to: filter.le && DateFilterManager.convertDateForEdit(filter.le),
from: filter.ge ? {
dateString: DateFilterManager.convertDateForEdit(filter.ge),
isValid: true,
} : undefined,
to: filter.le ? {
dateString: DateFilterManager.convertDateForEdit(filter.le),
isValid: true,
} : undefined,
};
}
static calculateRelativeRangeValueState(filter: DateFilterData) {
Expand Down Expand Up @@ -74,7 +80,7 @@ export class DateFilterManager extends React.Component<Props, State> {
};
}

handleCommitValue = (value: ?Object) => {
handleCommitValue = (value: ?Value) => {
this.setState({ value });
this.props.handleCommitValue && this.props.handleCommitValue();
};
Expand Down
Loading
Loading