@dhis2-ui/calendar 10.0.0-alpha.5 → 10.0.0-alpha.7
Sign up to get free protection for your applications and to get access to all the features.
- package/build/cjs/calendar/calendar.js +3 -8
- package/build/cjs/calendar-input/__tests__/calendar-input.test.js +220 -3
- package/build/cjs/calendar-input/calendar-input.js +36 -27
- package/build/cjs/stories/calendar-story-wrapper.js +3 -8
- package/build/es/calendar/calendar.js +3 -8
- package/build/es/calendar-input/__tests__/calendar-input.test.js +218 -3
- package/build/es/calendar-input/calendar-input.js +36 -27
- package/build/es/stories/calendar-story-wrapper.js +3 -8
- package/package.json +8 -8
@@ -19,10 +19,10 @@ const Calendar = _ref => {
|
|
19
19
|
dir,
|
20
20
|
locale,
|
21
21
|
numberingSystem,
|
22
|
-
weekDayFormat,
|
22
|
+
weekDayFormat = 'narrow',
|
23
23
|
timeZone,
|
24
|
-
width,
|
25
|
-
cellSize
|
24
|
+
width = '240px',
|
25
|
+
cellSize = '32px'
|
26
26
|
} = _ref;
|
27
27
|
const [selectedDateString, setSelectedDateString] = (0, _react.useState)(date);
|
28
28
|
const languageDirection = (0, _multiCalendarDates.useResolvedDirection)(dir, locale);
|
@@ -70,11 +70,6 @@ const Calendar = _ref => {
|
|
70
70
|
return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_calendarContainer.CalendarContainer, calendarProps));
|
71
71
|
};
|
72
72
|
exports.Calendar = Calendar;
|
73
|
-
Calendar.defaultProps = {
|
74
|
-
cellSize: '32px',
|
75
|
-
width: '240px',
|
76
|
-
weekDayFormat: 'narrow'
|
77
|
-
};
|
78
73
|
const CalendarProps = exports.CalendarProps = {
|
79
74
|
/** the calendar to use such gregory, ethiopic, nepali - full supported list here: https://github.com/dhis2/multi-calendar-dates/blob/main/src/constants/calendars.ts */
|
80
75
|
calendar: _propTypes.default.any.isRequired,
|
@@ -1,10 +1,21 @@
|
|
1
1
|
"use strict";
|
2
2
|
|
3
|
+
var _button = require("@dhis2-ui/button");
|
3
4
|
var _react = require("@testing-library/react");
|
4
|
-
var
|
5
|
+
var _userEvent = _interopRequireDefault(require("@testing-library/user-event"));
|
6
|
+
var _react2 = _interopRequireWildcard(require("react"));
|
7
|
+
var _reactFinalForm = require("react-final-form");
|
5
8
|
var _calendarInput = require("../calendar-input.js");
|
9
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
10
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
6
11
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
12
|
+
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
7
13
|
describe('Calendar Input', () => {
|
14
|
+
beforeEach(() => {
|
15
|
+
jest.useFakeTimers();
|
16
|
+
jest.setSystemTime(new Date('2024-10-22T09:05:00.000Z'));
|
17
|
+
});
|
18
|
+
afterEach(jest.useRealTimers);
|
8
19
|
it('allow selection of a date through the calendar widget', async () => {
|
9
20
|
const onDateSelectMock = jest.fn();
|
10
21
|
const screen = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_calendarInput.CalendarInput, {
|
@@ -15,7 +26,7 @@ describe('Calendar Input', () => {
|
|
15
26
|
_react.fireEvent.focus(dateInput);
|
16
27
|
const calendar = await screen.findByTestId('calendar');
|
17
28
|
expect(calendar).toBeInTheDocument();
|
18
|
-
const todayString =
|
29
|
+
const todayString = '2024-10-22';
|
19
30
|
const today = (0, _react.within)(calendar).getByTestId(todayString);
|
20
31
|
_react.fireEvent.click(today);
|
21
32
|
await (0, _react.waitFor)(() => {
|
@@ -43,4 +54,210 @@ describe('Calendar Input', () => {
|
|
43
54
|
calendarDateString: dateInputString
|
44
55
|
}));
|
45
56
|
});
|
46
|
-
|
57
|
+
describe('validation', () => {
|
58
|
+
it('should validate minimum date', async () => {
|
59
|
+
const onDateSelectMock = jest.fn();
|
60
|
+
const screen = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(CalendarWithValidation, {
|
61
|
+
calendar: "gregory",
|
62
|
+
minDate: "2024-01-01",
|
63
|
+
onDateSelect: onDateSelectMock
|
64
|
+
}));
|
65
|
+
const dateInputString = '2023-10-12';
|
66
|
+
const dateInput = (0, _react.within)(screen.getByTestId('dhis2-uicore-input')).getByRole('textbox');
|
67
|
+
_userEvent.default.clear(dateInput);
|
68
|
+
_userEvent.default.type(dateInput, dateInputString);
|
69
|
+
_userEvent.default.tab();
|
70
|
+
expect(await screen.findByText('Date 2023-10-12 is less than the minimum allowed date 2024-01-01.'));
|
71
|
+
expect(onDateSelectMock).toHaveBeenCalledTimes(1);
|
72
|
+
});
|
73
|
+
it('should validate maximum date', async () => {
|
74
|
+
const {
|
75
|
+
getByTestId,
|
76
|
+
findByText
|
77
|
+
} = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(CalendarWithValidation, {
|
78
|
+
calendar: "gregory",
|
79
|
+
maxDate: "2024-01-01"
|
80
|
+
}));
|
81
|
+
const dateInputString = '2024-10-12';
|
82
|
+
const dateInput = (0, _react.within)(getByTestId('dhis2-uicore-input')).getByRole('textbox');
|
83
|
+
_userEvent.default.clear(dateInput);
|
84
|
+
_userEvent.default.type(dateInput, dateInputString);
|
85
|
+
_userEvent.default.tab();
|
86
|
+
expect(await findByText('Date 2024-10-12 is greater than the maximum allowed date 2024-01-01.'));
|
87
|
+
});
|
88
|
+
it('should validate date in ethiopic calendar', async () => {
|
89
|
+
const onDateSelectMock = jest.fn();
|
90
|
+
const {
|
91
|
+
getByTestId,
|
92
|
+
findByText,
|
93
|
+
queryByText
|
94
|
+
} = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(CalendarWithValidation, {
|
95
|
+
calendar: "ethiopian",
|
96
|
+
minDate: "2018-13-04",
|
97
|
+
onDateSelect: onDateSelectMock
|
98
|
+
}));
|
99
|
+
let dateInputString = '2018-13-02';
|
100
|
+
const dateInput = (0, _react.within)(getByTestId('dhis2-uicore-input')).getByRole('textbox');
|
101
|
+
_userEvent.default.clear(dateInput);
|
102
|
+
_userEvent.default.type(dateInput, dateInputString);
|
103
|
+
_userEvent.default.tab();
|
104
|
+
expect(await findByText('Date 2018-13-02 is less than the minimum allowed date 2018-13-04.'));
|
105
|
+
dateInputString = '2018-13-05';
|
106
|
+
_userEvent.default.clear(dateInput);
|
107
|
+
_userEvent.default.type(dateInput, dateInputString);
|
108
|
+
_userEvent.default.tab();
|
109
|
+
expect(queryByText('Date 2018-13-04 is less than the minimum allowed date 2018-13-05.')).not.toBeInTheDocument();
|
110
|
+
dateInputString = '2018-13-07';
|
111
|
+
_userEvent.default.clear(dateInput);
|
112
|
+
_userEvent.default.type(dateInput, dateInputString);
|
113
|
+
_userEvent.default.tab();
|
114
|
+
expect(await findByText('Invalid date in specified calendar')).toBeInTheDocument();
|
115
|
+
});
|
116
|
+
it('should validate date in nepali calendar', async () => {
|
117
|
+
const onDateSelectMock = jest.fn();
|
118
|
+
const {
|
119
|
+
getByTestId,
|
120
|
+
findByText,
|
121
|
+
queryByText
|
122
|
+
} = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(CalendarWithValidation, {
|
123
|
+
calendar: "nepali",
|
124
|
+
maxDate: "2080-05-30",
|
125
|
+
onDateSelect: onDateSelectMock
|
126
|
+
}));
|
127
|
+
let dateInputString = '2080-06-01';
|
128
|
+
const dateInput = (0, _react.within)(getByTestId('dhis2-uicore-input')).getByRole('textbox');
|
129
|
+
_userEvent.default.clear(dateInput);
|
130
|
+
_userEvent.default.type(dateInput, dateInputString);
|
131
|
+
_userEvent.default.tab();
|
132
|
+
expect(await findByText('Date 2080-06-01 is greater than the maximum allowed date 2080-05-30.'));
|
133
|
+
dateInputString = '2080-04-32';
|
134
|
+
_userEvent.default.clear(dateInput);
|
135
|
+
_userEvent.default.type(dateInput, dateInputString);
|
136
|
+
_userEvent.default.tab();
|
137
|
+
expect(queryByText(/greater than the maximum allowed date/)).not.toBeInTheDocument();
|
138
|
+
dateInputString = '2080-01-32';
|
139
|
+
_userEvent.default.clear(dateInput);
|
140
|
+
_userEvent.default.type(dateInput, dateInputString);
|
141
|
+
_userEvent.default.tab();
|
142
|
+
expect(await findByText('Invalid date in specified calendar')).toBeInTheDocument();
|
143
|
+
});
|
144
|
+
it('should validate from date picker', async () => {
|
145
|
+
const onDateSelectMock = jest.fn();
|
146
|
+
const {
|
147
|
+
queryByText,
|
148
|
+
getByText,
|
149
|
+
getByTestId
|
150
|
+
} = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(CalendarWithValidation, {
|
151
|
+
calendar: "gregory",
|
152
|
+
minDate: "2024-02-16",
|
153
|
+
onDateSelect: onDateSelectMock
|
154
|
+
}));
|
155
|
+
const dateInput = (0, _react.within)(getByTestId('dhis2-uicore-input')).getByRole('textbox');
|
156
|
+
await _userEvent.default.click(dateInput);
|
157
|
+
await _userEvent.default.click(getByText('17'));
|
158
|
+
expect(queryByText('17')).not.toBeInTheDocument();
|
159
|
+
|
160
|
+
// Bug where callback used to be called first with undefined
|
161
|
+
expect(onDateSelectMock).toHaveBeenCalledTimes(1);
|
162
|
+
expect(onDateSelectMock).toHaveBeenCalledWith({
|
163
|
+
calendarDateString: '2024-10-17',
|
164
|
+
validation: {
|
165
|
+
error: false,
|
166
|
+
valid: true,
|
167
|
+
warning: false
|
168
|
+
}
|
169
|
+
});
|
170
|
+
});
|
171
|
+
it('should validate with Clear', async () => {
|
172
|
+
const onDateSelectMock = jest.fn();
|
173
|
+
const {
|
174
|
+
queryByText,
|
175
|
+
getByText,
|
176
|
+
getByTestId
|
177
|
+
} = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(CalendarWithValidation, {
|
178
|
+
calendar: "gregory",
|
179
|
+
minDate: "2024-02-16",
|
180
|
+
onDateSelect: onDateSelectMock,
|
181
|
+
clearable: true
|
182
|
+
}));
|
183
|
+
const dateInputString = '2023-10-12';
|
184
|
+
const dateInput = (0, _react.within)(getByTestId('dhis2-uicore-input')).getByRole('textbox');
|
185
|
+
_userEvent.default.clear(dateInput);
|
186
|
+
_userEvent.default.type(dateInput, dateInputString);
|
187
|
+
_userEvent.default.tab();
|
188
|
+
expect(getByTestId('dhis2-uiwidgets-calendar-inputfield-validation')).toBeInTheDocument();
|
189
|
+
await _userEvent.default.click(getByText('Clear'));
|
190
|
+
expect(queryByText('17')).not.toBeInTheDocument();
|
191
|
+
expect(onDateSelectMock).toHaveBeenLastCalledWith({
|
192
|
+
calendarDateString: null,
|
193
|
+
validation: {
|
194
|
+
valid: true
|
195
|
+
}
|
196
|
+
});
|
197
|
+
});
|
198
|
+
it('should validate when Clearing manually (i.e. deleting text not using clear button)', async () => {
|
199
|
+
const onDateSelectMock = jest.fn();
|
200
|
+
const {
|
201
|
+
getByTestId
|
202
|
+
} = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(CalendarWithValidation, {
|
203
|
+
calendar: "gregory",
|
204
|
+
minDate: "2024-02-16",
|
205
|
+
onDateSelect: onDateSelectMock,
|
206
|
+
clearable: true
|
207
|
+
}));
|
208
|
+
const dateInputString = '2023-10-12';
|
209
|
+
const dateInput = (0, _react.within)(getByTestId('dhis2-uicore-input')).getByRole('textbox');
|
210
|
+
_userEvent.default.clear(dateInput);
|
211
|
+
_userEvent.default.type(dateInput, dateInputString);
|
212
|
+
_userEvent.default.tab();
|
213
|
+
expect(getByTestId('dhis2-uiwidgets-calendar-inputfield-validation')).toBeInTheDocument();
|
214
|
+
_userEvent.default.clear(dateInput);
|
215
|
+
_userEvent.default.tab();
|
216
|
+
expect(onDateSelectMock).toHaveBeenCalledWith({
|
217
|
+
calendarDateString: null,
|
218
|
+
validation: {
|
219
|
+
valid: true
|
220
|
+
}
|
221
|
+
});
|
222
|
+
});
|
223
|
+
});
|
224
|
+
});
|
225
|
+
const CalendarWithValidation = propsFromParent => {
|
226
|
+
const [date, setDate] = (0, _react2.useState)();
|
227
|
+
const [validation, setValidation] = (0, _react2.useState)({});
|
228
|
+
const errored = () => {
|
229
|
+
if (validation !== null && validation !== void 0 && validation.error) {
|
230
|
+
return {
|
231
|
+
calendar: validation.validationText
|
232
|
+
};
|
233
|
+
}
|
234
|
+
};
|
235
|
+
return /*#__PURE__*/_react2.default.createElement(_reactFinalForm.Form, {
|
236
|
+
onSubmit: () => {},
|
237
|
+
validate: errored
|
238
|
+
}, _ref => {
|
239
|
+
let {
|
240
|
+
handleSubmit,
|
241
|
+
invalid
|
242
|
+
} = _ref;
|
243
|
+
return /*#__PURE__*/_react2.default.createElement("form", null, /*#__PURE__*/_react2.default.createElement(_reactFinalForm.Field, {
|
244
|
+
name: "calendar"
|
245
|
+
}, props => /*#__PURE__*/_react2.default.createElement(_calendarInput.CalendarInput, _extends({}, props, {
|
246
|
+
date: date,
|
247
|
+
label: "Enter a date",
|
248
|
+
editable: true,
|
249
|
+
calendar: "gregory"
|
250
|
+
}, validation, propsFromParent, {
|
251
|
+
onDateSelect: date => {
|
252
|
+
var _propsFromParent$onDa;
|
253
|
+
setDate(date === null || date === void 0 ? void 0 : date.calendarDateString);
|
254
|
+
setValidation(date === null || date === void 0 ? void 0 : date.validation);
|
255
|
+
(_propsFromParent$onDa = propsFromParent.onDateSelect) === null || _propsFromParent$onDa === void 0 ? void 0 : _propsFromParent$onDa.call(propsFromParent, date);
|
256
|
+
}
|
257
|
+
}))), /*#__PURE__*/_react2.default.createElement(_button.Button, {
|
258
|
+
type: "submit",
|
259
|
+
disabled: invalid,
|
260
|
+
onClick: handleSubmit
|
261
|
+
}, "Submit"));
|
262
|
+
});
|
263
|
+
};
|
@@ -43,6 +43,7 @@ const CalendarInput = function () {
|
|
43
43
|
format,
|
44
44
|
strictValidation,
|
45
45
|
inputWidth,
|
46
|
+
dataTest = 'dhis2-uiwidgets-calendar-inputfield',
|
46
47
|
...rest
|
47
48
|
} = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
48
49
|
const ref = (0, _react.useRef)();
|
@@ -56,26 +57,43 @@ const CalendarInput = function () {
|
|
56
57
|
numberingSystem,
|
57
58
|
weekDayFormat
|
58
59
|
}), [calendar, locale, numberingSystem, weekDayFormat]);
|
60
|
+
const onChooseDate = (date, validationOptions) => {
|
61
|
+
// Handling clearing (with clicking the Clear button, or deleting input)
|
62
|
+
if (clearable && (date === null || date === '')) {
|
63
|
+
parentOnDateSelect === null || parentOnDateSelect === void 0 ? void 0 : parentOnDateSelect({
|
64
|
+
calendarDateString: null,
|
65
|
+
validation: {
|
66
|
+
valid: true
|
67
|
+
}
|
68
|
+
});
|
69
|
+
return;
|
70
|
+
}
|
71
|
+
|
72
|
+
// ToDo: This is now a workaround for handling choosing from the date picker
|
73
|
+
// where the blur event gets triggered causing a call with undefined first
|
74
|
+
if (date === undefined) {
|
75
|
+
return;
|
76
|
+
}
|
77
|
+
const validation = (0, _multiCalendarDates.validateDateString)(date, validationOptions);
|
78
|
+
parentOnDateSelect === null || parentOnDateSelect === void 0 ? void 0 : parentOnDateSelect({
|
79
|
+
calendarDateString: date,
|
80
|
+
validation
|
81
|
+
});
|
82
|
+
};
|
83
|
+
const validationOptions = (0, _react.useMemo)(() => ({
|
84
|
+
calendar,
|
85
|
+
format,
|
86
|
+
minDateString: minDate,
|
87
|
+
maxDateString: maxDate,
|
88
|
+
strictValidation
|
89
|
+
}), [calendar, format, maxDate, minDate, strictValidation]);
|
59
90
|
const pickerResults = (0, _multiCalendarDates.useDatePicker)({
|
60
91
|
onDateSelect: result => {
|
61
|
-
|
62
|
-
calendar,
|
63
|
-
format,
|
64
|
-
minDateString: minDate,
|
65
|
-
maxDateString: maxDate,
|
66
|
-
strictValidation
|
67
|
-
});
|
92
|
+
onChooseDate(result.calendarDateString, validationOptions);
|
68
93
|
setOpen(false);
|
69
|
-
parentOnDateSelect === null || parentOnDateSelect === void 0 ? void 0 : parentOnDateSelect({
|
70
|
-
calendarDateString: result.calendarDateString,
|
71
|
-
validation
|
72
|
-
});
|
73
94
|
},
|
74
95
|
date,
|
75
|
-
|
76
|
-
maxDate: maxDate,
|
77
|
-
strictValidation: strictValidation,
|
78
|
-
format: format,
|
96
|
+
...validationOptions,
|
79
97
|
options: useDatePickerOptions
|
80
98
|
});
|
81
99
|
const handleChange = e => {
|
@@ -83,17 +101,7 @@ const CalendarInput = function () {
|
|
83
101
|
setPartialDate(e.value);
|
84
102
|
};
|
85
103
|
const handleBlur = (_, e) => {
|
86
|
-
|
87
|
-
calendar,
|
88
|
-
format,
|
89
|
-
minDateString: minDate,
|
90
|
-
maxDateString: maxDate,
|
91
|
-
strictValidation
|
92
|
-
});
|
93
|
-
parentOnDateSelect === null || parentOnDateSelect === void 0 ? void 0 : parentOnDateSelect({
|
94
|
-
calendarDateString: partialDate,
|
95
|
-
validation
|
96
|
-
});
|
104
|
+
onChooseDate(partialDate, validationOptions);
|
97
105
|
if (excludeRef.current && !excludeRef.current.contains(e.relatedTarget)) {
|
98
106
|
setOpen(false);
|
99
107
|
}
|
@@ -127,6 +135,7 @@ const CalendarInput = function () {
|
|
127
135
|
}, /*#__PURE__*/_react.default.createElement(_input.InputField, _extends({
|
128
136
|
label: _index.default.t('Pick a date')
|
129
137
|
}, rest, {
|
138
|
+
dataTest: dataTest,
|
130
139
|
type: "text",
|
131
140
|
onFocus: onFocus,
|
132
141
|
value: partialDate,
|
@@ -143,7 +152,7 @@ const CalendarInput = function () {
|
|
143
152
|
secondary: true,
|
144
153
|
small: true,
|
145
154
|
onClick: () => {
|
146
|
-
|
155
|
+
onChooseDate(null);
|
147
156
|
},
|
148
157
|
type: "button"
|
149
158
|
}, _index.default.t('Clear')))), open && /*#__PURE__*/_react.default.createElement(_layer.Layer, {
|
@@ -18,13 +18,13 @@ const {
|
|
18
18
|
} = _multiCalendarDates.constants;
|
19
19
|
const CalendarStoryWrapper = props => {
|
20
20
|
const {
|
21
|
-
calendar,
|
21
|
+
calendar = 'gregory',
|
22
22
|
locale,
|
23
23
|
timeZone,
|
24
24
|
dir,
|
25
|
-
component: Component,
|
25
|
+
component: Component = _calendar.Calendar,
|
26
26
|
date,
|
27
|
-
weekDayFormat
|
27
|
+
weekDayFormat = 'narrow'
|
28
28
|
} = props;
|
29
29
|
const [selectedCalendar, setSelectedCalendar] = (0, _react.useState)(calendar);
|
30
30
|
const [selectedNumberingSystem, setSelectedNumberingSystem] = (0, _react.useState)();
|
@@ -161,11 +161,6 @@ const CalendarStoryWrapper = props => {
|
|
161
161
|
}, selectedDate.calendarDateString)), /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("label", null, "callback:"), JSON.stringify(selectedDate, null, 2))))));
|
162
162
|
};
|
163
163
|
exports.CalendarStoryWrapper = CalendarStoryWrapper;
|
164
|
-
CalendarStoryWrapper.defaultProps = {
|
165
|
-
calendar: 'gregorian',
|
166
|
-
component: _calendar.Calendar,
|
167
|
-
weekDayFormat: 'narrow'
|
168
|
-
};
|
169
164
|
CalendarStoryWrapper.propTypes = {
|
170
165
|
calendar: _propTypes.default.string.isRequired,
|
171
166
|
component: _propTypes.default.elementType.isRequired,
|
@@ -10,10 +10,10 @@ export const Calendar = _ref => {
|
|
10
10
|
dir,
|
11
11
|
locale,
|
12
12
|
numberingSystem,
|
13
|
-
weekDayFormat,
|
13
|
+
weekDayFormat = 'narrow',
|
14
14
|
timeZone,
|
15
|
-
width,
|
16
|
-
cellSize
|
15
|
+
width = '240px',
|
16
|
+
cellSize = '32px'
|
17
17
|
} = _ref;
|
18
18
|
const [selectedDateString, setSelectedDateString] = useState(date);
|
19
19
|
const languageDirection = useResolvedDirection(dir, locale);
|
@@ -60,11 +60,6 @@ export const Calendar = _ref => {
|
|
60
60
|
}, [cellSize, date, dir, locale, pickerResults, width, languageDirection]);
|
61
61
|
return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(CalendarContainer, calendarProps));
|
62
62
|
};
|
63
|
-
Calendar.defaultProps = {
|
64
|
-
cellSize: '32px',
|
65
|
-
width: '240px',
|
66
|
-
weekDayFormat: 'narrow'
|
67
|
-
};
|
68
63
|
export const CalendarProps = {
|
69
64
|
/** the calendar to use such gregory, ethiopic, nepali - full supported list here: https://github.com/dhis2/multi-calendar-dates/blob/main/src/constants/calendars.ts */
|
70
65
|
calendar: PropTypes.any.isRequired,
|
@@ -1,7 +1,16 @@
|
|
1
|
+
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
2
|
+
import { Button } from '@dhis2-ui/button';
|
1
3
|
import { fireEvent, render, waitFor, within } from '@testing-library/react';
|
2
|
-
import
|
4
|
+
import userEvent from '@testing-library/user-event';
|
5
|
+
import React, { useState } from 'react';
|
6
|
+
import { Field, Form } from 'react-final-form';
|
3
7
|
import { CalendarInput } from '../calendar-input.js';
|
4
8
|
describe('Calendar Input', () => {
|
9
|
+
beforeEach(() => {
|
10
|
+
jest.useFakeTimers();
|
11
|
+
jest.setSystemTime(new Date('2024-10-22T09:05:00.000Z'));
|
12
|
+
});
|
13
|
+
afterEach(jest.useRealTimers);
|
5
14
|
it('allow selection of a date through the calendar widget', async () => {
|
6
15
|
const onDateSelectMock = jest.fn();
|
7
16
|
const screen = render( /*#__PURE__*/React.createElement(CalendarInput, {
|
@@ -12,7 +21,7 @@ describe('Calendar Input', () => {
|
|
12
21
|
fireEvent.focus(dateInput);
|
13
22
|
const calendar = await screen.findByTestId('calendar');
|
14
23
|
expect(calendar).toBeInTheDocument();
|
15
|
-
const todayString =
|
24
|
+
const todayString = '2024-10-22';
|
16
25
|
const today = within(calendar).getByTestId(todayString);
|
17
26
|
fireEvent.click(today);
|
18
27
|
await waitFor(() => {
|
@@ -40,4 +49,210 @@ describe('Calendar Input', () => {
|
|
40
49
|
calendarDateString: dateInputString
|
41
50
|
}));
|
42
51
|
});
|
43
|
-
|
52
|
+
describe('validation', () => {
|
53
|
+
it('should validate minimum date', async () => {
|
54
|
+
const onDateSelectMock = jest.fn();
|
55
|
+
const screen = render( /*#__PURE__*/React.createElement(CalendarWithValidation, {
|
56
|
+
calendar: "gregory",
|
57
|
+
minDate: "2024-01-01",
|
58
|
+
onDateSelect: onDateSelectMock
|
59
|
+
}));
|
60
|
+
const dateInputString = '2023-10-12';
|
61
|
+
const dateInput = within(screen.getByTestId('dhis2-uicore-input')).getByRole('textbox');
|
62
|
+
userEvent.clear(dateInput);
|
63
|
+
userEvent.type(dateInput, dateInputString);
|
64
|
+
userEvent.tab();
|
65
|
+
expect(await screen.findByText('Date 2023-10-12 is less than the minimum allowed date 2024-01-01.'));
|
66
|
+
expect(onDateSelectMock).toHaveBeenCalledTimes(1);
|
67
|
+
});
|
68
|
+
it('should validate maximum date', async () => {
|
69
|
+
const {
|
70
|
+
getByTestId,
|
71
|
+
findByText
|
72
|
+
} = render( /*#__PURE__*/React.createElement(CalendarWithValidation, {
|
73
|
+
calendar: "gregory",
|
74
|
+
maxDate: "2024-01-01"
|
75
|
+
}));
|
76
|
+
const dateInputString = '2024-10-12';
|
77
|
+
const dateInput = within(getByTestId('dhis2-uicore-input')).getByRole('textbox');
|
78
|
+
userEvent.clear(dateInput);
|
79
|
+
userEvent.type(dateInput, dateInputString);
|
80
|
+
userEvent.tab();
|
81
|
+
expect(await findByText('Date 2024-10-12 is greater than the maximum allowed date 2024-01-01.'));
|
82
|
+
});
|
83
|
+
it('should validate date in ethiopic calendar', async () => {
|
84
|
+
const onDateSelectMock = jest.fn();
|
85
|
+
const {
|
86
|
+
getByTestId,
|
87
|
+
findByText,
|
88
|
+
queryByText
|
89
|
+
} = render( /*#__PURE__*/React.createElement(CalendarWithValidation, {
|
90
|
+
calendar: "ethiopian",
|
91
|
+
minDate: "2018-13-04",
|
92
|
+
onDateSelect: onDateSelectMock
|
93
|
+
}));
|
94
|
+
let dateInputString = '2018-13-02';
|
95
|
+
const dateInput = within(getByTestId('dhis2-uicore-input')).getByRole('textbox');
|
96
|
+
userEvent.clear(dateInput);
|
97
|
+
userEvent.type(dateInput, dateInputString);
|
98
|
+
userEvent.tab();
|
99
|
+
expect(await findByText('Date 2018-13-02 is less than the minimum allowed date 2018-13-04.'));
|
100
|
+
dateInputString = '2018-13-05';
|
101
|
+
userEvent.clear(dateInput);
|
102
|
+
userEvent.type(dateInput, dateInputString);
|
103
|
+
userEvent.tab();
|
104
|
+
expect(queryByText('Date 2018-13-04 is less than the minimum allowed date 2018-13-05.')).not.toBeInTheDocument();
|
105
|
+
dateInputString = '2018-13-07';
|
106
|
+
userEvent.clear(dateInput);
|
107
|
+
userEvent.type(dateInput, dateInputString);
|
108
|
+
userEvent.tab();
|
109
|
+
expect(await findByText('Invalid date in specified calendar')).toBeInTheDocument();
|
110
|
+
});
|
111
|
+
it('should validate date in nepali calendar', async () => {
|
112
|
+
const onDateSelectMock = jest.fn();
|
113
|
+
const {
|
114
|
+
getByTestId,
|
115
|
+
findByText,
|
116
|
+
queryByText
|
117
|
+
} = render( /*#__PURE__*/React.createElement(CalendarWithValidation, {
|
118
|
+
calendar: "nepali",
|
119
|
+
maxDate: "2080-05-30",
|
120
|
+
onDateSelect: onDateSelectMock
|
121
|
+
}));
|
122
|
+
let dateInputString = '2080-06-01';
|
123
|
+
const dateInput = within(getByTestId('dhis2-uicore-input')).getByRole('textbox');
|
124
|
+
userEvent.clear(dateInput);
|
125
|
+
userEvent.type(dateInput, dateInputString);
|
126
|
+
userEvent.tab();
|
127
|
+
expect(await findByText('Date 2080-06-01 is greater than the maximum allowed date 2080-05-30.'));
|
128
|
+
dateInputString = '2080-04-32';
|
129
|
+
userEvent.clear(dateInput);
|
130
|
+
userEvent.type(dateInput, dateInputString);
|
131
|
+
userEvent.tab();
|
132
|
+
expect(queryByText(/greater than the maximum allowed date/)).not.toBeInTheDocument();
|
133
|
+
dateInputString = '2080-01-32';
|
134
|
+
userEvent.clear(dateInput);
|
135
|
+
userEvent.type(dateInput, dateInputString);
|
136
|
+
userEvent.tab();
|
137
|
+
expect(await findByText('Invalid date in specified calendar')).toBeInTheDocument();
|
138
|
+
});
|
139
|
+
it('should validate from date picker', async () => {
|
140
|
+
const onDateSelectMock = jest.fn();
|
141
|
+
const {
|
142
|
+
queryByText,
|
143
|
+
getByText,
|
144
|
+
getByTestId
|
145
|
+
} = render( /*#__PURE__*/React.createElement(CalendarWithValidation, {
|
146
|
+
calendar: "gregory",
|
147
|
+
minDate: "2024-02-16",
|
148
|
+
onDateSelect: onDateSelectMock
|
149
|
+
}));
|
150
|
+
const dateInput = within(getByTestId('dhis2-uicore-input')).getByRole('textbox');
|
151
|
+
await userEvent.click(dateInput);
|
152
|
+
await userEvent.click(getByText('17'));
|
153
|
+
expect(queryByText('17')).not.toBeInTheDocument();
|
154
|
+
|
155
|
+
// Bug where callback used to be called first with undefined
|
156
|
+
expect(onDateSelectMock).toHaveBeenCalledTimes(1);
|
157
|
+
expect(onDateSelectMock).toHaveBeenCalledWith({
|
158
|
+
calendarDateString: '2024-10-17',
|
159
|
+
validation: {
|
160
|
+
error: false,
|
161
|
+
valid: true,
|
162
|
+
warning: false
|
163
|
+
}
|
164
|
+
});
|
165
|
+
});
|
166
|
+
it('should validate with Clear', async () => {
|
167
|
+
const onDateSelectMock = jest.fn();
|
168
|
+
const {
|
169
|
+
queryByText,
|
170
|
+
getByText,
|
171
|
+
getByTestId
|
172
|
+
} = render( /*#__PURE__*/React.createElement(CalendarWithValidation, {
|
173
|
+
calendar: "gregory",
|
174
|
+
minDate: "2024-02-16",
|
175
|
+
onDateSelect: onDateSelectMock,
|
176
|
+
clearable: true
|
177
|
+
}));
|
178
|
+
const dateInputString = '2023-10-12';
|
179
|
+
const dateInput = within(getByTestId('dhis2-uicore-input')).getByRole('textbox');
|
180
|
+
userEvent.clear(dateInput);
|
181
|
+
userEvent.type(dateInput, dateInputString);
|
182
|
+
userEvent.tab();
|
183
|
+
expect(getByTestId('dhis2-uiwidgets-calendar-inputfield-validation')).toBeInTheDocument();
|
184
|
+
await userEvent.click(getByText('Clear'));
|
185
|
+
expect(queryByText('17')).not.toBeInTheDocument();
|
186
|
+
expect(onDateSelectMock).toHaveBeenLastCalledWith({
|
187
|
+
calendarDateString: null,
|
188
|
+
validation: {
|
189
|
+
valid: true
|
190
|
+
}
|
191
|
+
});
|
192
|
+
});
|
193
|
+
it('should validate when Clearing manually (i.e. deleting text not using clear button)', async () => {
|
194
|
+
const onDateSelectMock = jest.fn();
|
195
|
+
const {
|
196
|
+
getByTestId
|
197
|
+
} = render( /*#__PURE__*/React.createElement(CalendarWithValidation, {
|
198
|
+
calendar: "gregory",
|
199
|
+
minDate: "2024-02-16",
|
200
|
+
onDateSelect: onDateSelectMock,
|
201
|
+
clearable: true
|
202
|
+
}));
|
203
|
+
const dateInputString = '2023-10-12';
|
204
|
+
const dateInput = within(getByTestId('dhis2-uicore-input')).getByRole('textbox');
|
205
|
+
userEvent.clear(dateInput);
|
206
|
+
userEvent.type(dateInput, dateInputString);
|
207
|
+
userEvent.tab();
|
208
|
+
expect(getByTestId('dhis2-uiwidgets-calendar-inputfield-validation')).toBeInTheDocument();
|
209
|
+
userEvent.clear(dateInput);
|
210
|
+
userEvent.tab();
|
211
|
+
expect(onDateSelectMock).toHaveBeenCalledWith({
|
212
|
+
calendarDateString: null,
|
213
|
+
validation: {
|
214
|
+
valid: true
|
215
|
+
}
|
216
|
+
});
|
217
|
+
});
|
218
|
+
});
|
219
|
+
});
|
220
|
+
const CalendarWithValidation = propsFromParent => {
|
221
|
+
const [date, setDate] = useState();
|
222
|
+
const [validation, setValidation] = useState({});
|
223
|
+
const errored = () => {
|
224
|
+
if (validation !== null && validation !== void 0 && validation.error) {
|
225
|
+
return {
|
226
|
+
calendar: validation.validationText
|
227
|
+
};
|
228
|
+
}
|
229
|
+
};
|
230
|
+
return /*#__PURE__*/React.createElement(Form, {
|
231
|
+
onSubmit: () => {},
|
232
|
+
validate: errored
|
233
|
+
}, _ref => {
|
234
|
+
let {
|
235
|
+
handleSubmit,
|
236
|
+
invalid
|
237
|
+
} = _ref;
|
238
|
+
return /*#__PURE__*/React.createElement("form", null, /*#__PURE__*/React.createElement(Field, {
|
239
|
+
name: "calendar"
|
240
|
+
}, props => /*#__PURE__*/React.createElement(CalendarInput, _extends({}, props, {
|
241
|
+
date: date,
|
242
|
+
label: "Enter a date",
|
243
|
+
editable: true,
|
244
|
+
calendar: "gregory"
|
245
|
+
}, validation, propsFromParent, {
|
246
|
+
onDateSelect: date => {
|
247
|
+
var _propsFromParent$onDa;
|
248
|
+
setDate(date === null || date === void 0 ? void 0 : date.calendarDateString);
|
249
|
+
setValidation(date === null || date === void 0 ? void 0 : date.validation);
|
250
|
+
(_propsFromParent$onDa = propsFromParent.onDateSelect) === null || _propsFromParent$onDa === void 0 ? void 0 : _propsFromParent$onDa.call(propsFromParent, date);
|
251
|
+
}
|
252
|
+
}))), /*#__PURE__*/React.createElement(Button, {
|
253
|
+
type: "submit",
|
254
|
+
disabled: invalid,
|
255
|
+
onClick: handleSubmit
|
256
|
+
}, "Submit"));
|
257
|
+
});
|
258
|
+
};
|
@@ -34,6 +34,7 @@ export const CalendarInput = function () {
|
|
34
34
|
format,
|
35
35
|
strictValidation,
|
36
36
|
inputWidth,
|
37
|
+
dataTest = 'dhis2-uiwidgets-calendar-inputfield',
|
37
38
|
...rest
|
38
39
|
} = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
39
40
|
const ref = useRef();
|
@@ -47,26 +48,43 @@ export const CalendarInput = function () {
|
|
47
48
|
numberingSystem,
|
48
49
|
weekDayFormat
|
49
50
|
}), [calendar, locale, numberingSystem, weekDayFormat]);
|
51
|
+
const onChooseDate = (date, validationOptions) => {
|
52
|
+
// Handling clearing (with clicking the Clear button, or deleting input)
|
53
|
+
if (clearable && (date === null || date === '')) {
|
54
|
+
parentOnDateSelect === null || parentOnDateSelect === void 0 ? void 0 : parentOnDateSelect({
|
55
|
+
calendarDateString: null,
|
56
|
+
validation: {
|
57
|
+
valid: true
|
58
|
+
}
|
59
|
+
});
|
60
|
+
return;
|
61
|
+
}
|
62
|
+
|
63
|
+
// ToDo: This is now a workaround for handling choosing from the date picker
|
64
|
+
// where the blur event gets triggered causing a call with undefined first
|
65
|
+
if (date === undefined) {
|
66
|
+
return;
|
67
|
+
}
|
68
|
+
const validation = validateDateString(date, validationOptions);
|
69
|
+
parentOnDateSelect === null || parentOnDateSelect === void 0 ? void 0 : parentOnDateSelect({
|
70
|
+
calendarDateString: date,
|
71
|
+
validation
|
72
|
+
});
|
73
|
+
};
|
74
|
+
const validationOptions = useMemo(() => ({
|
75
|
+
calendar,
|
76
|
+
format,
|
77
|
+
minDateString: minDate,
|
78
|
+
maxDateString: maxDate,
|
79
|
+
strictValidation
|
80
|
+
}), [calendar, format, maxDate, minDate, strictValidation]);
|
50
81
|
const pickerResults = useDatePicker({
|
51
82
|
onDateSelect: result => {
|
52
|
-
|
53
|
-
calendar,
|
54
|
-
format,
|
55
|
-
minDateString: minDate,
|
56
|
-
maxDateString: maxDate,
|
57
|
-
strictValidation
|
58
|
-
});
|
83
|
+
onChooseDate(result.calendarDateString, validationOptions);
|
59
84
|
setOpen(false);
|
60
|
-
parentOnDateSelect === null || parentOnDateSelect === void 0 ? void 0 : parentOnDateSelect({
|
61
|
-
calendarDateString: result.calendarDateString,
|
62
|
-
validation
|
63
|
-
});
|
64
85
|
},
|
65
86
|
date,
|
66
|
-
|
67
|
-
maxDate: maxDate,
|
68
|
-
strictValidation: strictValidation,
|
69
|
-
format: format,
|
87
|
+
...validationOptions,
|
70
88
|
options: useDatePickerOptions
|
71
89
|
});
|
72
90
|
const handleChange = e => {
|
@@ -74,17 +92,7 @@ export const CalendarInput = function () {
|
|
74
92
|
setPartialDate(e.value);
|
75
93
|
};
|
76
94
|
const handleBlur = (_, e) => {
|
77
|
-
|
78
|
-
calendar,
|
79
|
-
format,
|
80
|
-
minDateString: minDate,
|
81
|
-
maxDateString: maxDate,
|
82
|
-
strictValidation
|
83
|
-
});
|
84
|
-
parentOnDateSelect === null || parentOnDateSelect === void 0 ? void 0 : parentOnDateSelect({
|
85
|
-
calendarDateString: partialDate,
|
86
|
-
validation
|
87
|
-
});
|
95
|
+
onChooseDate(partialDate, validationOptions);
|
88
96
|
if (excludeRef.current && !excludeRef.current.contains(e.relatedTarget)) {
|
89
97
|
setOpen(false);
|
90
98
|
}
|
@@ -118,6 +126,7 @@ export const CalendarInput = function () {
|
|
118
126
|
}, /*#__PURE__*/React.createElement(InputField, _extends({
|
119
127
|
label: i18n.t('Pick a date')
|
120
128
|
}, rest, {
|
129
|
+
dataTest: dataTest,
|
121
130
|
type: "text",
|
122
131
|
onFocus: onFocus,
|
123
132
|
value: partialDate,
|
@@ -134,7 +143,7 @@ export const CalendarInput = function () {
|
|
134
143
|
secondary: true,
|
135
144
|
small: true,
|
136
145
|
onClick: () => {
|
137
|
-
|
146
|
+
onChooseDate(null);
|
138
147
|
},
|
139
148
|
type: "button"
|
140
149
|
}, i18n.t('Clear')))), open && /*#__PURE__*/React.createElement(Layer, {
|
@@ -9,13 +9,13 @@ const {
|
|
9
9
|
} = constants;
|
10
10
|
export const CalendarStoryWrapper = props => {
|
11
11
|
const {
|
12
|
-
calendar,
|
12
|
+
calendar = 'gregory',
|
13
13
|
locale,
|
14
14
|
timeZone,
|
15
15
|
dir,
|
16
|
-
component: Component,
|
16
|
+
component: Component = Calendar,
|
17
17
|
date,
|
18
|
-
weekDayFormat
|
18
|
+
weekDayFormat = 'narrow'
|
19
19
|
} = props;
|
20
20
|
const [selectedCalendar, setSelectedCalendar] = useState(calendar);
|
21
21
|
const [selectedNumberingSystem, setSelectedNumberingSystem] = useState();
|
@@ -151,11 +151,6 @@ export const CalendarStoryWrapper = props => {
|
|
151
151
|
"data-test": "storybook-calendar-result"
|
152
152
|
}, selectedDate.calendarDateString)), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("label", null, "callback:"), JSON.stringify(selectedDate, null, 2))))));
|
153
153
|
};
|
154
|
-
CalendarStoryWrapper.defaultProps = {
|
155
|
-
calendar: 'gregorian',
|
156
|
-
component: Calendar,
|
157
|
-
weekDayFormat: 'narrow'
|
158
|
-
};
|
159
154
|
CalendarStoryWrapper.propTypes = {
|
160
155
|
calendar: PropTypes.string.isRequired,
|
161
156
|
component: PropTypes.elementType.isRequired,
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@dhis2-ui/calendar",
|
3
|
-
"version": "10.0.0-alpha.
|
3
|
+
"version": "10.0.0-alpha.7",
|
4
4
|
"description": "UI Calendar",
|
5
5
|
"repository": {
|
6
6
|
"type": "git",
|
@@ -33,15 +33,15 @@
|
|
33
33
|
"styled-jsx": "^4"
|
34
34
|
},
|
35
35
|
"dependencies": {
|
36
|
-
"@dhis2-ui/button": "10.0.0-alpha.
|
37
|
-
"@dhis2-ui/card": "10.0.0-alpha.
|
38
|
-
"@dhis2-ui/input": "10.0.0-alpha.
|
39
|
-
"@dhis2-ui/layer": "10.0.0-alpha.
|
40
|
-
"@dhis2-ui/popper": "10.0.0-alpha.
|
36
|
+
"@dhis2-ui/button": "10.0.0-alpha.7",
|
37
|
+
"@dhis2-ui/card": "10.0.0-alpha.7",
|
38
|
+
"@dhis2-ui/input": "10.0.0-alpha.7",
|
39
|
+
"@dhis2-ui/layer": "10.0.0-alpha.7",
|
40
|
+
"@dhis2-ui/popper": "10.0.0-alpha.7",
|
41
41
|
"@dhis2/multi-calendar-dates": "2.0.0-alpha.5",
|
42
42
|
"@dhis2/prop-types": "^3.1.2",
|
43
|
-
"@dhis2/ui-constants": "10.0.0-alpha.
|
44
|
-
"@dhis2/ui-icons": "10.0.0-alpha.
|
43
|
+
"@dhis2/ui-constants": "10.0.0-alpha.7",
|
44
|
+
"@dhis2/ui-icons": "10.0.0-alpha.7",
|
45
45
|
"classnames": "^2.3.1",
|
46
46
|
"prop-types": "^15.7.2"
|
47
47
|
},
|