@dhis2-ui/calendar 10.0.0-alpha.4 → 10.0.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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 _react2 = _interopRequireDefault(require("react"));
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 = new Date().toISOString().slice(0, -14);
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
+ };
@@ -56,26 +56,43 @@ const CalendarInput = function () {
56
56
  numberingSystem,
57
57
  weekDayFormat
58
58
  }), [calendar, locale, numberingSystem, weekDayFormat]);
59
+ const onChooseDate = (date, validationOptions) => {
60
+ // Handling clearing (with clicking the Clear button, or deleting input)
61
+ if (clearable && (date === null || date === '')) {
62
+ parentOnDateSelect === null || parentOnDateSelect === void 0 ? void 0 : parentOnDateSelect({
63
+ calendarDateString: null,
64
+ validation: {
65
+ valid: true
66
+ }
67
+ });
68
+ return;
69
+ }
70
+
71
+ // ToDo: This is now a workaround for handling choosing from the date picker
72
+ // where the blur event gets triggered causing a call with undefined first
73
+ if (date === undefined) {
74
+ return;
75
+ }
76
+ const validation = (0, _multiCalendarDates.validateDateString)(date, validationOptions);
77
+ parentOnDateSelect === null || parentOnDateSelect === void 0 ? void 0 : parentOnDateSelect({
78
+ calendarDateString: date,
79
+ validation
80
+ });
81
+ };
82
+ const validationOptions = (0, _react.useMemo)(() => ({
83
+ calendar,
84
+ format,
85
+ minDateString: minDate,
86
+ maxDateString: maxDate,
87
+ strictValidation
88
+ }), [calendar, format, maxDate, minDate, strictValidation]);
59
89
  const pickerResults = (0, _multiCalendarDates.useDatePicker)({
60
90
  onDateSelect: result => {
61
- const validation = (0, _multiCalendarDates.validateDateString)(result.calendarDateString, {
62
- calendar,
63
- format,
64
- minDateString: minDate,
65
- maxDateString: maxDate,
66
- strictValidation
67
- });
91
+ onChooseDate(result.calendarDateString, validationOptions);
68
92
  setOpen(false);
69
- parentOnDateSelect === null || parentOnDateSelect === void 0 ? void 0 : parentOnDateSelect({
70
- calendarDateString: result.calendarDateString,
71
- validation
72
- });
73
93
  },
74
94
  date,
75
- minDate: minDate,
76
- maxDate: maxDate,
77
- strictValidation: strictValidation,
78
- format: format,
95
+ ...validationOptions,
79
96
  options: useDatePickerOptions
80
97
  });
81
98
  const handleChange = e => {
@@ -83,23 +100,15 @@ const CalendarInput = function () {
83
100
  setPartialDate(e.value);
84
101
  };
85
102
  const handleBlur = (_, e) => {
86
- const validation = (0, _multiCalendarDates.validateDateString)(partialDate, {
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
- });
103
+ onChooseDate(partialDate, validationOptions);
97
104
  if (excludeRef.current && !excludeRef.current.contains(e.relatedTarget)) {
98
105
  setOpen(false);
99
106
  }
100
107
  };
101
108
  const onFocus = () => {
109
+ var _rest$onFocus;
102
110
  setOpen(true);
111
+ rest === null || rest === void 0 ? void 0 : (_rest$onFocus = rest.onFocus) === null || _rest$onFocus === void 0 ? void 0 : _rest$onFocus.call(rest);
103
112
  };
104
113
  const languageDirection = (0, _multiCalendarDates.useResolvedDirection)(dir, locale);
105
114
  const calendarProps = (0, _react.useMemo)(() => {
@@ -141,7 +150,7 @@ const CalendarInput = function () {
141
150
  secondary: true,
142
151
  small: true,
143
152
  onClick: () => {
144
- parentOnDateSelect === null || parentOnDateSelect === void 0 ? void 0 : parentOnDateSelect(null);
153
+ onChooseDate(null);
145
154
  },
146
155
  type: "button"
147
156
  }, _index.default.t('Clear')))), open && /*#__PURE__*/_react.default.createElement(_layer.Layer, {
@@ -131,6 +131,9 @@ function CalendarWithEditiableInput() {
131
131
  const date = selectedDate === null || selectedDate === void 0 ? void 0 : selectedDate.calendarDateString;
132
132
  setDate(date);
133
133
  },
134
+ onFocus: () => {
135
+ console.log('focused');
136
+ },
134
137
  width: "400px",
135
138
  minDate: "2020-07-01",
136
139
  maxDate: "2020-07-09",
@@ -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 React from 'react';
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 = new Date().toISOString().slice(0, -14);
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
+ };
@@ -47,26 +47,43 @@ export const CalendarInput = function () {
47
47
  numberingSystem,
48
48
  weekDayFormat
49
49
  }), [calendar, locale, numberingSystem, weekDayFormat]);
50
+ const onChooseDate = (date, validationOptions) => {
51
+ // Handling clearing (with clicking the Clear button, or deleting input)
52
+ if (clearable && (date === null || date === '')) {
53
+ parentOnDateSelect === null || parentOnDateSelect === void 0 ? void 0 : parentOnDateSelect({
54
+ calendarDateString: null,
55
+ validation: {
56
+ valid: true
57
+ }
58
+ });
59
+ return;
60
+ }
61
+
62
+ // ToDo: This is now a workaround for handling choosing from the date picker
63
+ // where the blur event gets triggered causing a call with undefined first
64
+ if (date === undefined) {
65
+ return;
66
+ }
67
+ const validation = validateDateString(date, validationOptions);
68
+ parentOnDateSelect === null || parentOnDateSelect === void 0 ? void 0 : parentOnDateSelect({
69
+ calendarDateString: date,
70
+ validation
71
+ });
72
+ };
73
+ const validationOptions = useMemo(() => ({
74
+ calendar,
75
+ format,
76
+ minDateString: minDate,
77
+ maxDateString: maxDate,
78
+ strictValidation
79
+ }), [calendar, format, maxDate, minDate, strictValidation]);
50
80
  const pickerResults = useDatePicker({
51
81
  onDateSelect: result => {
52
- const validation = validateDateString(result.calendarDateString, {
53
- calendar,
54
- format,
55
- minDateString: minDate,
56
- maxDateString: maxDate,
57
- strictValidation
58
- });
82
+ onChooseDate(result.calendarDateString, validationOptions);
59
83
  setOpen(false);
60
- parentOnDateSelect === null || parentOnDateSelect === void 0 ? void 0 : parentOnDateSelect({
61
- calendarDateString: result.calendarDateString,
62
- validation
63
- });
64
84
  },
65
85
  date,
66
- minDate: minDate,
67
- maxDate: maxDate,
68
- strictValidation: strictValidation,
69
- format: format,
86
+ ...validationOptions,
70
87
  options: useDatePickerOptions
71
88
  });
72
89
  const handleChange = e => {
@@ -74,23 +91,15 @@ export const CalendarInput = function () {
74
91
  setPartialDate(e.value);
75
92
  };
76
93
  const handleBlur = (_, e) => {
77
- const validation = validateDateString(partialDate, {
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
- });
94
+ onChooseDate(partialDate, validationOptions);
88
95
  if (excludeRef.current && !excludeRef.current.contains(e.relatedTarget)) {
89
96
  setOpen(false);
90
97
  }
91
98
  };
92
99
  const onFocus = () => {
100
+ var _rest$onFocus;
93
101
  setOpen(true);
102
+ rest === null || rest === void 0 ? void 0 : (_rest$onFocus = rest.onFocus) === null || _rest$onFocus === void 0 ? void 0 : _rest$onFocus.call(rest);
94
103
  };
95
104
  const languageDirection = useResolvedDirection(dir, locale);
96
105
  const calendarProps = useMemo(() => {
@@ -132,7 +141,7 @@ export const CalendarInput = function () {
132
141
  secondary: true,
133
142
  small: true,
134
143
  onClick: () => {
135
- parentOnDateSelect === null || parentOnDateSelect === void 0 ? void 0 : parentOnDateSelect(null);
144
+ onChooseDate(null);
136
145
  },
137
146
  type: "button"
138
147
  }, i18n.t('Clear')))), open && /*#__PURE__*/React.createElement(Layer, {
@@ -117,6 +117,9 @@ export function CalendarWithEditiableInput() {
117
117
  const date = selectedDate === null || selectedDate === void 0 ? void 0 : selectedDate.calendarDateString;
118
118
  setDate(date);
119
119
  },
120
+ onFocus: () => {
121
+ console.log('focused');
122
+ },
120
123
  width: "400px",
121
124
  minDate: "2020-07-01",
122
125
  maxDate: "2020-07-09",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dhis2-ui/calendar",
3
- "version": "10.0.0-alpha.4",
3
+ "version": "10.0.0-alpha.6",
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.4",
37
- "@dhis2-ui/card": "10.0.0-alpha.4",
38
- "@dhis2-ui/input": "10.0.0-alpha.4",
39
- "@dhis2-ui/layer": "10.0.0-alpha.4",
40
- "@dhis2-ui/popper": "10.0.0-alpha.4",
41
- "@dhis2/multi-calendar-dates": "2.0.0-alpha.2",
36
+ "@dhis2-ui/button": "10.0.0-alpha.6",
37
+ "@dhis2-ui/card": "10.0.0-alpha.6",
38
+ "@dhis2-ui/input": "10.0.0-alpha.6",
39
+ "@dhis2-ui/layer": "10.0.0-alpha.6",
40
+ "@dhis2-ui/popper": "10.0.0-alpha.6",
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.4",
44
- "@dhis2/ui-icons": "10.0.0-alpha.4",
43
+ "@dhis2/ui-constants": "10.0.0-alpha.6",
44
+ "@dhis2/ui-icons": "10.0.0-alpha.6",
45
45
  "classnames": "^2.3.1",
46
46
  "prop-types": "^15.7.2"
47
47
  },