@dhis2-ui/calendar 9.9.0-alpha.3 → 9.9.0-alpha.4
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.
- package/build/cjs/calendar/calendar-container.js +17 -5
- package/build/cjs/calendar/calendar-table-cell.js +5 -2
- package/build/cjs/calendar/calendar-table.js +5 -2
- package/build/cjs/calendar/navigation-container.js +8 -2
- package/build/cjs/calendar-input/__tests__/calendar-input.test.js +57 -0
- package/build/cjs/calendar-input/calendar-input.js +18 -3
- package/build/es/calendar/calendar-container.js +18 -5
- package/build/es/calendar/calendar-table-cell.js +5 -2
- package/build/es/calendar/calendar-table.js +5 -2
- package/build/es/calendar/navigation-container.js +8 -2
- package/build/es/calendar-input/__tests__/calendar-input.test.js +43 -0
- package/build/es/calendar-input/calendar-input.js +19 -4
- package/package.json +8 -8
|
@@ -23,6 +23,8 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
|
|
|
23
23
|
|
|
24
24
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
25
25
|
|
|
26
|
+
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
27
|
+
|
|
26
28
|
const wrapperBorderColor = _uiConstants.colors.grey300;
|
|
27
29
|
const backgroundColor = 'none';
|
|
28
30
|
|
|
@@ -39,7 +41,9 @@ const CalendarContainer = _ref => {
|
|
|
39
41
|
nextYear,
|
|
40
42
|
prevMonth,
|
|
41
43
|
prevYear,
|
|
42
|
-
languageDirection
|
|
44
|
+
languageDirection,
|
|
45
|
+
excludedRef,
|
|
46
|
+
unfocusable
|
|
43
47
|
} = _ref;
|
|
44
48
|
const navigationProps = (0, _react.useMemo)(() => {
|
|
45
49
|
return {
|
|
@@ -58,13 +62,19 @@ const CalendarContainer = _ref => {
|
|
|
58
62
|
dir: languageDirection,
|
|
59
63
|
"data-test": "calendar",
|
|
60
64
|
className: _style.default.dynamic([["2823859047", [backgroundColor, wrapperBorderColor, width]]]) + " " + "calendar-wrapper"
|
|
61
|
-
}, /*#__PURE__*/_react.default.createElement(
|
|
65
|
+
}, /*#__PURE__*/_react.default.createElement("div", {
|
|
66
|
+
ref: excludedRef,
|
|
67
|
+
className: _style.default.dynamic([["2823859047", [backgroundColor, wrapperBorderColor, width]]])
|
|
68
|
+
}, /*#__PURE__*/_react.default.createElement(_navigationContainer.NavigationContainer, _extends({}, navigationProps, {
|
|
69
|
+
unfocusable: unfocusable
|
|
70
|
+
})), /*#__PURE__*/_react.default.createElement(_calendarTable.CalendarTable, {
|
|
62
71
|
selectedDate: date,
|
|
63
72
|
calendarWeekDays: calendarWeekDays,
|
|
64
73
|
weekDayLabels: weekDayLabels,
|
|
65
74
|
cellSize: cellSize,
|
|
66
|
-
width: width
|
|
67
|
-
|
|
75
|
+
width: width,
|
|
76
|
+
unfocusable: unfocusable
|
|
77
|
+
}))), /*#__PURE__*/_react.default.createElement(_style.default, {
|
|
68
78
|
id: "2823859047",
|
|
69
79
|
dynamic: [backgroundColor, wrapperBorderColor, width]
|
|
70
80
|
}, [".calendar-wrapper.__jsx-style-dynamic-selector{font-family:Roboto,sans-serif;font-weight:400;font-size:14px;background-color:".concat(backgroundColor, ";display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;border:1px solid ").concat(wrapperBorderColor, ";border-radius:3px;min-width:").concat(width, ";width:-webkit-max-content;width:-moz-max-content;width:max-content;box-shadow:0px 4px 6px -2px #2129340d;box-shadow:0px 10px 15px -3px #2129341a;}")]));
|
|
@@ -73,11 +83,13 @@ const CalendarContainer = _ref => {
|
|
|
73
83
|
exports.CalendarContainer = CalendarContainer;
|
|
74
84
|
CalendarContainer.defaultProps = {
|
|
75
85
|
cellSize: '32px',
|
|
76
|
-
width: '240px'
|
|
86
|
+
width: '240px',
|
|
87
|
+
unfocusable: false
|
|
77
88
|
};
|
|
78
89
|
CalendarContainer.propTypes = {
|
|
79
90
|
/** the currently selected date using an iso-like format YYYY-MM-DD, in the calendar system provided (not iso8601) */
|
|
80
91
|
date: _propTypes.default.string,
|
|
92
|
+
unfocusable: _propTypes.default.bool,
|
|
81
93
|
..._calendarTable.CalendarTableProps,
|
|
82
94
|
..._navigationContainer.NavigationContainerProps
|
|
83
95
|
};
|
|
@@ -21,7 +21,8 @@ const CalendarTableCell = _ref => {
|
|
|
21
21
|
let {
|
|
22
22
|
day,
|
|
23
23
|
cellSize,
|
|
24
|
-
selectedDate
|
|
24
|
+
selectedDate,
|
|
25
|
+
unfocusable
|
|
25
26
|
} = _ref;
|
|
26
27
|
const dayHoverBackgroundColor = _uiConstants.colors.grey200;
|
|
27
28
|
const selectedDayBackgroundColor = _uiConstants.colors.teal700;
|
|
@@ -31,6 +32,7 @@ const CalendarTableCell = _ref => {
|
|
|
31
32
|
className: _style.default.dynamic([["2052411850", [cellSize, cellSize, cellSize, cellSize, _uiConstants.colors.grey900, dayHoverBackgroundColor, _uiConstants.colors.grey300, selectedDayBackgroundColor, _uiConstants.colors.teal600, _uiConstants.colors.teal200, _uiConstants.colors.grey600]]])
|
|
32
33
|
}, /*#__PURE__*/_react.default.createElement("button", {
|
|
33
34
|
name: "day",
|
|
35
|
+
tabIndex: unfocusable ? -1 : 0,
|
|
34
36
|
className: _style.default.dynamic([["2052411850", [cellSize, cellSize, cellSize, cellSize, _uiConstants.colors.grey900, dayHoverBackgroundColor, _uiConstants.colors.grey300, selectedDayBackgroundColor, _uiConstants.colors.teal600, _uiConstants.colors.teal200, _uiConstants.colors.grey600]]]) + " " + ((0, _classnames.default)('day', {
|
|
35
37
|
isSelected: selectedDate === (day === null || day === void 0 ? void 0 : day.calendarDate),
|
|
36
38
|
isToday: day.isToday,
|
|
@@ -53,5 +55,6 @@ CalendarTableCell.propTypes = {
|
|
|
53
55
|
label: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number]),
|
|
54
56
|
onClick: _propTypes.default.func
|
|
55
57
|
}),
|
|
56
|
-
selectedDate: _propTypes.default.string
|
|
58
|
+
selectedDate: _propTypes.default.string,
|
|
59
|
+
unfocusable: _propTypes.default.bool
|
|
57
60
|
};
|
|
@@ -25,7 +25,8 @@ const CalendarTable = _ref => {
|
|
|
25
25
|
calendarWeekDays,
|
|
26
26
|
width,
|
|
27
27
|
cellSize,
|
|
28
|
-
selectedDate
|
|
28
|
+
selectedDate,
|
|
29
|
+
unfocusable
|
|
29
30
|
} = _ref;
|
|
30
31
|
return /*#__PURE__*/_react.default.createElement("div", {
|
|
31
32
|
className: _style.default.dynamic([["452536960", [_uiConstants.spacers.dp4, _uiConstants.spacers.dp4]]]) + " " + "calendar-table-wrapper"
|
|
@@ -43,7 +44,8 @@ const CalendarTable = _ref => {
|
|
|
43
44
|
day: day,
|
|
44
45
|
key: day === null || day === void 0 ? void 0 : day.calendarDate,
|
|
45
46
|
cellSize: cellSize,
|
|
46
|
-
width: width
|
|
47
|
+
width: width,
|
|
48
|
+
unfocusable: unfocusable
|
|
47
49
|
})))))), /*#__PURE__*/_react.default.createElement(_style.default, {
|
|
48
50
|
id: "452536960",
|
|
49
51
|
dynamic: [_uiConstants.spacers.dp4, _uiConstants.spacers.dp4]
|
|
@@ -63,6 +65,7 @@ const CalendarTableProps = {
|
|
|
63
65
|
}).isRequired).isRequired).isRequired,
|
|
64
66
|
cellSize: _propTypes.default.string,
|
|
65
67
|
selectedDate: _propTypes.default.string,
|
|
68
|
+
unfocusable: _propTypes.default.bool,
|
|
66
69
|
weekDayLabels: _propTypes.default.arrayOf(_propTypes.default.string),
|
|
67
70
|
width: _propTypes.default.string
|
|
68
71
|
};
|
|
@@ -32,7 +32,8 @@ const NavigationContainer = _ref => {
|
|
|
32
32
|
nextMonth,
|
|
33
33
|
nextYear,
|
|
34
34
|
prevMonth,
|
|
35
|
-
prevYear
|
|
35
|
+
prevYear,
|
|
36
|
+
unfocusable
|
|
36
37
|
} = _ref;
|
|
37
38
|
const PreviousIcon = languageDirection === 'ltr' ? _uiIcons.IconChevronLeft16 : _uiIcons.IconChevronRight16;
|
|
38
39
|
const NextIcon = languageDirection === 'ltr' ? _uiIcons.IconChevronRight16 : _uiIcons.IconChevronLeft16; // Ethiopic years - when localised to English - add the era (i.e. 2015 ERA1), which is redundant in practice (like writing AD for gregorian years)
|
|
@@ -51,6 +52,7 @@ const NavigationContainer = _ref => {
|
|
|
51
52
|
"data-test": "calendar-previous-month",
|
|
52
53
|
"aria-label": "".concat(_index.default.t("Go to ".concat(prevMonth.label))),
|
|
53
54
|
type: "button",
|
|
55
|
+
tabIndex: unfocusable ? -1 : 0,
|
|
54
56
|
className: _style.default.dynamic([["3883083596", [_uiConstants.spacers.dp4, _uiConstants.colors.grey600, _uiConstants.colors.grey200, _uiConstants.colors.grey900, _uiConstants.colors.grey300, _uiConstants.spacers.dp8, _uiConstants.spacers.dp4, wrapperBorderColor, headerBackground]]])
|
|
55
57
|
}, /*#__PURE__*/_react.default.createElement(PreviousIcon, {
|
|
56
58
|
className: _style.default.dynamic([["3883083596", [_uiConstants.spacers.dp4, _uiConstants.colors.grey600, _uiConstants.colors.grey200, _uiConstants.colors.grey900, _uiConstants.colors.grey300, _uiConstants.spacers.dp8, _uiConstants.spacers.dp4, wrapperBorderColor, headerBackground]]])
|
|
@@ -66,6 +68,7 @@ const NavigationContainer = _ref => {
|
|
|
66
68
|
name: "next-month",
|
|
67
69
|
"aria-label": "".concat(_index.default.t("Go to ".concat(nextMonth.label))),
|
|
68
70
|
type: "button",
|
|
71
|
+
tabIndex: unfocusable ? -1 : 0,
|
|
69
72
|
className: _style.default.dynamic([["3883083596", [_uiConstants.spacers.dp4, _uiConstants.colors.grey600, _uiConstants.colors.grey200, _uiConstants.colors.grey900, _uiConstants.colors.grey300, _uiConstants.spacers.dp8, _uiConstants.spacers.dp4, wrapperBorderColor, headerBackground]]])
|
|
70
73
|
}, /*#__PURE__*/_react.default.createElement(NextIcon, {
|
|
71
74
|
className: _style.default.dynamic([["3883083596", [_uiConstants.spacers.dp4, _uiConstants.colors.grey600, _uiConstants.colors.grey200, _uiConstants.colors.grey900, _uiConstants.colors.grey300, _uiConstants.spacers.dp8, _uiConstants.spacers.dp4, wrapperBorderColor, headerBackground]]])
|
|
@@ -78,6 +81,7 @@ const NavigationContainer = _ref => {
|
|
|
78
81
|
name: "previous-year",
|
|
79
82
|
"aria-label": "".concat(_index.default.t('Go to previous year')),
|
|
80
83
|
type: "button",
|
|
84
|
+
tabIndex: unfocusable ? -1 : 0,
|
|
81
85
|
className: _style.default.dynamic([["3883083596", [_uiConstants.spacers.dp4, _uiConstants.colors.grey600, _uiConstants.colors.grey200, _uiConstants.colors.grey900, _uiConstants.colors.grey300, _uiConstants.spacers.dp8, _uiConstants.spacers.dp4, wrapperBorderColor, headerBackground]]])
|
|
82
86
|
}, /*#__PURE__*/_react.default.createElement(PreviousIcon, {
|
|
83
87
|
className: _style.default.dynamic([["3883083596", [_uiConstants.spacers.dp4, _uiConstants.colors.grey600, _uiConstants.colors.grey200, _uiConstants.colors.grey900, _uiConstants.colors.grey300, _uiConstants.spacers.dp8, _uiConstants.spacers.dp4, wrapperBorderColor, headerBackground]]])
|
|
@@ -93,6 +97,7 @@ const NavigationContainer = _ref => {
|
|
|
93
97
|
name: "next-year",
|
|
94
98
|
"aria-label": "".concat(_index.default.t('Go to next year')),
|
|
95
99
|
type: "button",
|
|
100
|
+
tabIndex: unfocusable ? -1 : 0,
|
|
96
101
|
className: _style.default.dynamic([["3883083596", [_uiConstants.spacers.dp4, _uiConstants.colors.grey600, _uiConstants.colors.grey200, _uiConstants.colors.grey900, _uiConstants.colors.grey300, _uiConstants.spacers.dp8, _uiConstants.spacers.dp4, wrapperBorderColor, headerBackground]]])
|
|
97
102
|
}, /*#__PURE__*/_react.default.createElement(NextIcon, {
|
|
98
103
|
className: _style.default.dynamic([["3883083596", [_uiConstants.spacers.dp4, _uiConstants.colors.grey600, _uiConstants.colors.grey200, _uiConstants.colors.grey900, _uiConstants.colors.grey300, _uiConstants.spacers.dp8, _uiConstants.spacers.dp4, wrapperBorderColor, headerBackground]]])
|
|
@@ -126,7 +131,8 @@ const NavigationContainerProps = {
|
|
|
126
131
|
prevYear: _propTypes.default.shape({
|
|
127
132
|
label: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number]),
|
|
128
133
|
navigateTo: _propTypes.default.func
|
|
129
|
-
})
|
|
134
|
+
}),
|
|
135
|
+
unfocusable: _propTypes.default.bool
|
|
130
136
|
};
|
|
131
137
|
exports.NavigationContainerProps = NavigationContainerProps;
|
|
132
138
|
NavigationContainer.propTypes = NavigationContainerProps;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _react = require("@testing-library/react");
|
|
4
|
+
|
|
5
|
+
var _react2 = _interopRequireDefault(require("react"));
|
|
6
|
+
|
|
7
|
+
var _calendarInput = require("../calendar-input.js");
|
|
8
|
+
|
|
9
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
10
|
+
|
|
11
|
+
describe('Calendar Input', () => {
|
|
12
|
+
it('allow selection of a date through the calendar widget', async () => {
|
|
13
|
+
const onDateSelectMock = jest.fn();
|
|
14
|
+
const screen = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_calendarInput.CalendarInput, {
|
|
15
|
+
calendar: "gregory",
|
|
16
|
+
onDateSelect: onDateSelectMock
|
|
17
|
+
}));
|
|
18
|
+
const dateInput = (0, _react.within)(screen.getByTestId('dhis2-uicore-input')).getByRole('textbox');
|
|
19
|
+
|
|
20
|
+
_react.fireEvent.focus(dateInput);
|
|
21
|
+
|
|
22
|
+
const calendar = await screen.findByTestId('calendar');
|
|
23
|
+
expect(calendar).toBeInTheDocument();
|
|
24
|
+
const todayString = new Date().toISOString().slice(0, -14);
|
|
25
|
+
const today = (0, _react.within)(calendar).getByTestId(todayString);
|
|
26
|
+
|
|
27
|
+
_react.fireEvent.click(today);
|
|
28
|
+
|
|
29
|
+
await (0, _react.waitFor)(() => {
|
|
30
|
+
expect(calendar).not.toBeInTheDocument();
|
|
31
|
+
});
|
|
32
|
+
expect(onDateSelectMock).toHaveBeenCalledWith(expect.objectContaining({
|
|
33
|
+
calendarDateString: todayString
|
|
34
|
+
}));
|
|
35
|
+
});
|
|
36
|
+
it('allow selection of a date through the input', async () => {
|
|
37
|
+
const onDateSelectMock = jest.fn();
|
|
38
|
+
const screen = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_calendarInput.CalendarInput, {
|
|
39
|
+
calendar: "gregory",
|
|
40
|
+
onDateSelect: onDateSelectMock
|
|
41
|
+
}));
|
|
42
|
+
const dateInputString = '2024/10/12';
|
|
43
|
+
const dateInput = (0, _react.within)(screen.getByTestId('dhis2-uicore-input')).getByRole('textbox');
|
|
44
|
+
|
|
45
|
+
_react.fireEvent.change(dateInput, {
|
|
46
|
+
target: {
|
|
47
|
+
value: dateInputString
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
_react.fireEvent.blur(dateInput);
|
|
52
|
+
|
|
53
|
+
expect(onDateSelectMock).toHaveBeenCalledWith(expect.objectContaining({
|
|
54
|
+
calendarDateString: dateInputString
|
|
55
|
+
}));
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -66,6 +66,9 @@ const CalendarInput = function () {
|
|
|
66
66
|
} = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
67
67
|
const ref = (0, _react.useRef)();
|
|
68
68
|
const [open, setOpen] = (0, _react.useState)(false);
|
|
69
|
+
const [partialDate, setPartialDate] = (0, _react.useState)(date);
|
|
70
|
+
const excludeRef = (0, _react.useRef)(null);
|
|
71
|
+
(0, _react.useEffect)(() => setPartialDate(date), [date]);
|
|
69
72
|
const useDatePickerOptions = (0, _react.useMemo)(() => ({
|
|
70
73
|
calendar,
|
|
71
74
|
locale,
|
|
@@ -88,9 +91,17 @@ const CalendarInput = function () {
|
|
|
88
91
|
});
|
|
89
92
|
|
|
90
93
|
const handleChange = e => {
|
|
94
|
+
setPartialDate(e.value);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const handleBlur = (_, e) => {
|
|
91
98
|
parentOnDateSelect === null || parentOnDateSelect === void 0 ? void 0 : parentOnDateSelect({
|
|
92
|
-
calendarDateString:
|
|
99
|
+
calendarDateString: partialDate
|
|
93
100
|
});
|
|
101
|
+
|
|
102
|
+
if (excludeRef.current && !excludeRef.current.contains(e.relatedTarget)) {
|
|
103
|
+
setOpen(false);
|
|
104
|
+
}
|
|
94
105
|
};
|
|
95
106
|
|
|
96
107
|
const onFocus = () => {
|
|
@@ -123,8 +134,9 @@ const CalendarInput = function () {
|
|
|
123
134
|
}, rest, {
|
|
124
135
|
type: "text",
|
|
125
136
|
onFocus: onFocus,
|
|
126
|
-
value:
|
|
137
|
+
value: partialDate,
|
|
127
138
|
onChange: handleChange,
|
|
139
|
+
onBlur: handleBlur,
|
|
128
140
|
validationText: pickerResults.errorMessage || pickerResults.warningMessage,
|
|
129
141
|
error: !!pickerResults.errorMessage,
|
|
130
142
|
warning: !!pickerResults.warningMessage
|
|
@@ -149,7 +161,10 @@ const CalendarInput = function () {
|
|
|
149
161
|
reference: ref,
|
|
150
162
|
placement: "bottom-start",
|
|
151
163
|
modifiers: [offsetModifier]
|
|
152
|
-
}, /*#__PURE__*/_react.default.createElement(_card.Card, null, /*#__PURE__*/_react.default.createElement(_calendarContainer.CalendarContainer,
|
|
164
|
+
}, /*#__PURE__*/_react.default.createElement(_card.Card, null, /*#__PURE__*/_react.default.createElement(_calendarContainer.CalendarContainer, _extends({}, calendarProps, {
|
|
165
|
+
excludedRef: excludeRef,
|
|
166
|
+
unfocusable: true
|
|
167
|
+
}))))), /*#__PURE__*/_react.default.createElement(_style.default, {
|
|
153
168
|
id: "633677374"
|
|
154
169
|
}, [".calendar-input-wrapper.jsx-633677374{position:relative;}", ".calendar-clear-button.jsx-633677374{position:absolute;inset-inline-end:6px;-webkit-inset-block-start:27px;-ms-intb-rlock-start:27px;inset-block-start:27px;}", ".calendar-clear-button.with-icon.jsx-633677374{inset-inline-end:36px;}", ".calendar-clear-button.with-dense-wrapper.jsx-633677374{-webkit-inset-block-start:23px;-ms-intb-rlock-start:23px;inset-block-start:23px;}"]));
|
|
155
170
|
};
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import _JSXStyle from "styled-jsx/style";
|
|
2
|
+
|
|
3
|
+
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
4
|
+
|
|
2
5
|
import { colors } from '@dhis2/ui-constants';
|
|
3
6
|
import PropTypes from 'prop-types';
|
|
4
7
|
import React, { useMemo } from 'react';
|
|
@@ -19,7 +22,9 @@ export const CalendarContainer = _ref => {
|
|
|
19
22
|
nextYear,
|
|
20
23
|
prevMonth,
|
|
21
24
|
prevYear,
|
|
22
|
-
languageDirection
|
|
25
|
+
languageDirection,
|
|
26
|
+
excludedRef,
|
|
27
|
+
unfocusable
|
|
23
28
|
} = _ref;
|
|
24
29
|
const navigationProps = useMemo(() => {
|
|
25
30
|
return {
|
|
@@ -38,24 +43,32 @@ export const CalendarContainer = _ref => {
|
|
|
38
43
|
dir: languageDirection,
|
|
39
44
|
"data-test": "calendar",
|
|
40
45
|
className: _JSXStyle.dynamic([["2823859047", [backgroundColor, wrapperBorderColor, width]]]) + " " + "calendar-wrapper"
|
|
41
|
-
}, /*#__PURE__*/React.createElement(
|
|
46
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
47
|
+
ref: excludedRef,
|
|
48
|
+
className: _JSXStyle.dynamic([["2823859047", [backgroundColor, wrapperBorderColor, width]]])
|
|
49
|
+
}, /*#__PURE__*/React.createElement(NavigationContainer, _extends({}, navigationProps, {
|
|
50
|
+
unfocusable: unfocusable
|
|
51
|
+
})), /*#__PURE__*/React.createElement(CalendarTable, {
|
|
42
52
|
selectedDate: date,
|
|
43
53
|
calendarWeekDays: calendarWeekDays,
|
|
44
54
|
weekDayLabels: weekDayLabels,
|
|
45
55
|
cellSize: cellSize,
|
|
46
|
-
width: width
|
|
47
|
-
|
|
56
|
+
width: width,
|
|
57
|
+
unfocusable: unfocusable
|
|
58
|
+
}))), /*#__PURE__*/React.createElement(_JSXStyle, {
|
|
48
59
|
id: "2823859047",
|
|
49
60
|
dynamic: [backgroundColor, wrapperBorderColor, width]
|
|
50
61
|
}, [".calendar-wrapper.__jsx-style-dynamic-selector{font-family:Roboto,sans-serif;font-weight:400;font-size:14px;background-color:".concat(backgroundColor, ";display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;border:1px solid ").concat(wrapperBorderColor, ";border-radius:3px;min-width:").concat(width, ";width:-webkit-max-content;width:-moz-max-content;width:max-content;box-shadow:0px 4px 6px -2px #2129340d;box-shadow:0px 10px 15px -3px #2129341a;}")]));
|
|
51
62
|
};
|
|
52
63
|
CalendarContainer.defaultProps = {
|
|
53
64
|
cellSize: '32px',
|
|
54
|
-
width: '240px'
|
|
65
|
+
width: '240px',
|
|
66
|
+
unfocusable: false
|
|
55
67
|
};
|
|
56
68
|
CalendarContainer.propTypes = {
|
|
57
69
|
/** the currently selected date using an iso-like format YYYY-MM-DD, in the calendar system provided (not iso8601) */
|
|
58
70
|
date: PropTypes.string,
|
|
71
|
+
unfocusable: PropTypes.bool,
|
|
59
72
|
...CalendarTableProps,
|
|
60
73
|
...NavigationContainerProps
|
|
61
74
|
};
|
|
@@ -7,7 +7,8 @@ export const CalendarTableCell = _ref => {
|
|
|
7
7
|
let {
|
|
8
8
|
day,
|
|
9
9
|
cellSize,
|
|
10
|
-
selectedDate
|
|
10
|
+
selectedDate,
|
|
11
|
+
unfocusable
|
|
11
12
|
} = _ref;
|
|
12
13
|
const dayHoverBackgroundColor = colors.grey200;
|
|
13
14
|
const selectedDayBackgroundColor = colors.teal700;
|
|
@@ -17,6 +18,7 @@ export const CalendarTableCell = _ref => {
|
|
|
17
18
|
className: _JSXStyle.dynamic([["2052411850", [cellSize, cellSize, cellSize, cellSize, colors.grey900, dayHoverBackgroundColor, colors.grey300, selectedDayBackgroundColor, colors.teal600, colors.teal200, colors.grey600]]])
|
|
18
19
|
}, /*#__PURE__*/React.createElement("button", {
|
|
19
20
|
name: "day",
|
|
21
|
+
tabIndex: unfocusable ? -1 : 0,
|
|
20
22
|
className: _JSXStyle.dynamic([["2052411850", [cellSize, cellSize, cellSize, cellSize, colors.grey900, dayHoverBackgroundColor, colors.grey300, selectedDayBackgroundColor, colors.teal600, colors.teal200, colors.grey600]]]) + " " + (cx('day', {
|
|
21
23
|
isSelected: selectedDate === (day === null || day === void 0 ? void 0 : day.calendarDate),
|
|
22
24
|
isToday: day.isToday,
|
|
@@ -37,5 +39,6 @@ CalendarTableCell.propTypes = {
|
|
|
37
39
|
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
38
40
|
onClick: PropTypes.func
|
|
39
41
|
}),
|
|
40
|
-
selectedDate: PropTypes.string
|
|
42
|
+
selectedDate: PropTypes.string,
|
|
43
|
+
unfocusable: PropTypes.bool
|
|
41
44
|
};
|
|
@@ -10,7 +10,8 @@ export const CalendarTable = _ref => {
|
|
|
10
10
|
calendarWeekDays,
|
|
11
11
|
width,
|
|
12
12
|
cellSize,
|
|
13
|
-
selectedDate
|
|
13
|
+
selectedDate,
|
|
14
|
+
unfocusable
|
|
14
15
|
} = _ref;
|
|
15
16
|
return /*#__PURE__*/React.createElement("div", {
|
|
16
17
|
className: _JSXStyle.dynamic([["452536960", [spacers.dp4, spacers.dp4]]]) + " " + "calendar-table-wrapper"
|
|
@@ -28,7 +29,8 @@ export const CalendarTable = _ref => {
|
|
|
28
29
|
day: day,
|
|
29
30
|
key: day === null || day === void 0 ? void 0 : day.calendarDate,
|
|
30
31
|
cellSize: cellSize,
|
|
31
|
-
width: width
|
|
32
|
+
width: width,
|
|
33
|
+
unfocusable: unfocusable
|
|
32
34
|
})))))), /*#__PURE__*/React.createElement(_JSXStyle, {
|
|
33
35
|
id: "452536960",
|
|
34
36
|
dynamic: [spacers.dp4, spacers.dp4]
|
|
@@ -46,6 +48,7 @@ export const CalendarTableProps = {
|
|
|
46
48
|
}).isRequired).isRequired).isRequired,
|
|
47
49
|
cellSize: PropTypes.string,
|
|
48
50
|
selectedDate: PropTypes.string,
|
|
51
|
+
unfocusable: PropTypes.bool,
|
|
49
52
|
weekDayLabels: PropTypes.arrayOf(PropTypes.string),
|
|
50
53
|
width: PropTypes.string
|
|
51
54
|
};
|
|
@@ -16,7 +16,8 @@ export const NavigationContainer = _ref => {
|
|
|
16
16
|
nextMonth,
|
|
17
17
|
nextYear,
|
|
18
18
|
prevMonth,
|
|
19
|
-
prevYear
|
|
19
|
+
prevYear,
|
|
20
|
+
unfocusable
|
|
20
21
|
} = _ref;
|
|
21
22
|
const PreviousIcon = languageDirection === 'ltr' ? IconChevronLeft16 : IconChevronRight16;
|
|
22
23
|
const NextIcon = languageDirection === 'ltr' ? IconChevronRight16 : IconChevronLeft16; // Ethiopic years - when localised to English - add the era (i.e. 2015 ERA1), which is redundant in practice (like writing AD for gregorian years)
|
|
@@ -35,6 +36,7 @@ export const NavigationContainer = _ref => {
|
|
|
35
36
|
"data-test": "calendar-previous-month",
|
|
36
37
|
"aria-label": "".concat(i18n.t("Go to ".concat(prevMonth.label))),
|
|
37
38
|
type: "button",
|
|
39
|
+
tabIndex: unfocusable ? -1 : 0,
|
|
38
40
|
className: _JSXStyle.dynamic([["3883083596", [spacers.dp4, colors.grey600, colors.grey200, colors.grey900, colors.grey300, spacers.dp8, spacers.dp4, wrapperBorderColor, headerBackground]]])
|
|
39
41
|
}, /*#__PURE__*/React.createElement(PreviousIcon, {
|
|
40
42
|
className: _JSXStyle.dynamic([["3883083596", [spacers.dp4, colors.grey600, colors.grey200, colors.grey900, colors.grey300, spacers.dp8, spacers.dp4, wrapperBorderColor, headerBackground]]])
|
|
@@ -50,6 +52,7 @@ export const NavigationContainer = _ref => {
|
|
|
50
52
|
name: "next-month",
|
|
51
53
|
"aria-label": "".concat(i18n.t("Go to ".concat(nextMonth.label))),
|
|
52
54
|
type: "button",
|
|
55
|
+
tabIndex: unfocusable ? -1 : 0,
|
|
53
56
|
className: _JSXStyle.dynamic([["3883083596", [spacers.dp4, colors.grey600, colors.grey200, colors.grey900, colors.grey300, spacers.dp8, spacers.dp4, wrapperBorderColor, headerBackground]]])
|
|
54
57
|
}, /*#__PURE__*/React.createElement(NextIcon, {
|
|
55
58
|
className: _JSXStyle.dynamic([["3883083596", [spacers.dp4, colors.grey600, colors.grey200, colors.grey900, colors.grey300, spacers.dp8, spacers.dp4, wrapperBorderColor, headerBackground]]])
|
|
@@ -62,6 +65,7 @@ export const NavigationContainer = _ref => {
|
|
|
62
65
|
name: "previous-year",
|
|
63
66
|
"aria-label": "".concat(i18n.t('Go to previous year')),
|
|
64
67
|
type: "button",
|
|
68
|
+
tabIndex: unfocusable ? -1 : 0,
|
|
65
69
|
className: _JSXStyle.dynamic([["3883083596", [spacers.dp4, colors.grey600, colors.grey200, colors.grey900, colors.grey300, spacers.dp8, spacers.dp4, wrapperBorderColor, headerBackground]]])
|
|
66
70
|
}, /*#__PURE__*/React.createElement(PreviousIcon, {
|
|
67
71
|
className: _JSXStyle.dynamic([["3883083596", [spacers.dp4, colors.grey600, colors.grey200, colors.grey900, colors.grey300, spacers.dp8, spacers.dp4, wrapperBorderColor, headerBackground]]])
|
|
@@ -77,6 +81,7 @@ export const NavigationContainer = _ref => {
|
|
|
77
81
|
name: "next-year",
|
|
78
82
|
"aria-label": "".concat(i18n.t('Go to next year')),
|
|
79
83
|
type: "button",
|
|
84
|
+
tabIndex: unfocusable ? -1 : 0,
|
|
80
85
|
className: _JSXStyle.dynamic([["3883083596", [spacers.dp4, colors.grey600, colors.grey200, colors.grey900, colors.grey300, spacers.dp8, spacers.dp4, wrapperBorderColor, headerBackground]]])
|
|
81
86
|
}, /*#__PURE__*/React.createElement(NextIcon, {
|
|
82
87
|
className: _JSXStyle.dynamic([["3883083596", [spacers.dp4, colors.grey600, colors.grey200, colors.grey900, colors.grey300, spacers.dp8, spacers.dp4, wrapperBorderColor, headerBackground]]])
|
|
@@ -108,6 +113,7 @@ export const NavigationContainerProps = {
|
|
|
108
113
|
prevYear: PropTypes.shape({
|
|
109
114
|
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
110
115
|
navigateTo: PropTypes.func
|
|
111
|
-
})
|
|
116
|
+
}),
|
|
117
|
+
unfocusable: PropTypes.bool
|
|
112
118
|
};
|
|
113
119
|
NavigationContainer.propTypes = NavigationContainerProps;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { fireEvent, render, waitFor, within } from '@testing-library/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { CalendarInput } from '../calendar-input.js';
|
|
4
|
+
describe('Calendar Input', () => {
|
|
5
|
+
it('allow selection of a date through the calendar widget', async () => {
|
|
6
|
+
const onDateSelectMock = jest.fn();
|
|
7
|
+
const screen = render( /*#__PURE__*/React.createElement(CalendarInput, {
|
|
8
|
+
calendar: "gregory",
|
|
9
|
+
onDateSelect: onDateSelectMock
|
|
10
|
+
}));
|
|
11
|
+
const dateInput = within(screen.getByTestId('dhis2-uicore-input')).getByRole('textbox');
|
|
12
|
+
fireEvent.focus(dateInput);
|
|
13
|
+
const calendar = await screen.findByTestId('calendar');
|
|
14
|
+
expect(calendar).toBeInTheDocument();
|
|
15
|
+
const todayString = new Date().toISOString().slice(0, -14);
|
|
16
|
+
const today = within(calendar).getByTestId(todayString);
|
|
17
|
+
fireEvent.click(today);
|
|
18
|
+
await waitFor(() => {
|
|
19
|
+
expect(calendar).not.toBeInTheDocument();
|
|
20
|
+
});
|
|
21
|
+
expect(onDateSelectMock).toHaveBeenCalledWith(expect.objectContaining({
|
|
22
|
+
calendarDateString: todayString
|
|
23
|
+
}));
|
|
24
|
+
});
|
|
25
|
+
it('allow selection of a date through the input', async () => {
|
|
26
|
+
const onDateSelectMock = jest.fn();
|
|
27
|
+
const screen = render( /*#__PURE__*/React.createElement(CalendarInput, {
|
|
28
|
+
calendar: "gregory",
|
|
29
|
+
onDateSelect: onDateSelectMock
|
|
30
|
+
}));
|
|
31
|
+
const dateInputString = '2024/10/12';
|
|
32
|
+
const dateInput = within(screen.getByTestId('dhis2-uicore-input')).getByRole('textbox');
|
|
33
|
+
fireEvent.change(dateInput, {
|
|
34
|
+
target: {
|
|
35
|
+
value: dateInputString
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
fireEvent.blur(dateInput);
|
|
39
|
+
expect(onDateSelectMock).toHaveBeenCalledWith(expect.objectContaining({
|
|
40
|
+
calendarDateString: dateInputString
|
|
41
|
+
}));
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -9,7 +9,7 @@ import { Layer } from '@dhis2-ui/layer';
|
|
|
9
9
|
import { Popper } from '@dhis2-ui/popper';
|
|
10
10
|
import { useDatePicker, useResolvedDirection } from '@dhis2/multi-calendar-dates';
|
|
11
11
|
import cx from 'classnames';
|
|
12
|
-
import React, { useRef, useState, useMemo } from 'react';
|
|
12
|
+
import React, { useRef, useState, useMemo, useEffect } from 'react';
|
|
13
13
|
import { CalendarContainer } from '../calendar/calendar-container.js';
|
|
14
14
|
import { CalendarProps } from '../calendar/calendar.js';
|
|
15
15
|
import i18n from '../locales/index.js';
|
|
@@ -41,6 +41,9 @@ export const CalendarInput = function () {
|
|
|
41
41
|
} = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
42
42
|
const ref = useRef();
|
|
43
43
|
const [open, setOpen] = useState(false);
|
|
44
|
+
const [partialDate, setPartialDate] = useState(date);
|
|
45
|
+
const excludeRef = useRef(null);
|
|
46
|
+
useEffect(() => setPartialDate(date), [date]);
|
|
44
47
|
const useDatePickerOptions = useMemo(() => ({
|
|
45
48
|
calendar,
|
|
46
49
|
locale,
|
|
@@ -63,9 +66,17 @@ export const CalendarInput = function () {
|
|
|
63
66
|
});
|
|
64
67
|
|
|
65
68
|
const handleChange = e => {
|
|
69
|
+
setPartialDate(e.value);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const handleBlur = (_, e) => {
|
|
66
73
|
parentOnDateSelect === null || parentOnDateSelect === void 0 ? void 0 : parentOnDateSelect({
|
|
67
|
-
calendarDateString:
|
|
74
|
+
calendarDateString: partialDate
|
|
68
75
|
});
|
|
76
|
+
|
|
77
|
+
if (excludeRef.current && !excludeRef.current.contains(e.relatedTarget)) {
|
|
78
|
+
setOpen(false);
|
|
79
|
+
}
|
|
69
80
|
};
|
|
70
81
|
|
|
71
82
|
const onFocus = () => {
|
|
@@ -98,8 +109,9 @@ export const CalendarInput = function () {
|
|
|
98
109
|
}, rest, {
|
|
99
110
|
type: "text",
|
|
100
111
|
onFocus: onFocus,
|
|
101
|
-
value:
|
|
112
|
+
value: partialDate,
|
|
102
113
|
onChange: handleChange,
|
|
114
|
+
onBlur: handleBlur,
|
|
103
115
|
validationText: pickerResults.errorMessage || pickerResults.warningMessage,
|
|
104
116
|
error: !!pickerResults.errorMessage,
|
|
105
117
|
warning: !!pickerResults.warningMessage
|
|
@@ -124,7 +136,10 @@ export const CalendarInput = function () {
|
|
|
124
136
|
reference: ref,
|
|
125
137
|
placement: "bottom-start",
|
|
126
138
|
modifiers: [offsetModifier]
|
|
127
|
-
}, /*#__PURE__*/React.createElement(Card, null, /*#__PURE__*/React.createElement(CalendarContainer,
|
|
139
|
+
}, /*#__PURE__*/React.createElement(Card, null, /*#__PURE__*/React.createElement(CalendarContainer, _extends({}, calendarProps, {
|
|
140
|
+
excludedRef: excludeRef,
|
|
141
|
+
unfocusable: true
|
|
142
|
+
}))))), /*#__PURE__*/React.createElement(_JSXStyle, {
|
|
128
143
|
id: "633677374"
|
|
129
144
|
}, [".calendar-input-wrapper.jsx-633677374{position:relative;}", ".calendar-clear-button.jsx-633677374{position:absolute;inset-inline-end:6px;-webkit-inset-block-start:27px;-ms-intb-rlock-start:27px;inset-block-start:27px;}", ".calendar-clear-button.with-icon.jsx-633677374{inset-inline-end:36px;}", ".calendar-clear-button.with-dense-wrapper.jsx-633677374{-webkit-inset-block-start:23px;-ms-intb-rlock-start:23px;inset-block-start:23px;}"]));
|
|
130
145
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dhis2-ui/calendar",
|
|
3
|
-
"version": "9.9.0-alpha.
|
|
3
|
+
"version": "9.9.0-alpha.4",
|
|
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": "9.9.0-alpha.
|
|
37
|
-
"@dhis2-ui/card": "9.9.0-alpha.
|
|
38
|
-
"@dhis2-ui/input": "9.9.0-alpha.
|
|
39
|
-
"@dhis2-ui/layer": "9.9.0-alpha.
|
|
40
|
-
"@dhis2-ui/popper": "9.9.0-alpha.
|
|
36
|
+
"@dhis2-ui/button": "9.9.0-alpha.4",
|
|
37
|
+
"@dhis2-ui/card": "9.9.0-alpha.4",
|
|
38
|
+
"@dhis2-ui/input": "9.9.0-alpha.4",
|
|
39
|
+
"@dhis2-ui/layer": "9.9.0-alpha.4",
|
|
40
|
+
"@dhis2-ui/popper": "9.9.0-alpha.4",
|
|
41
41
|
"@dhis2/multi-calendar-dates": "v1.0.0-alpha.27",
|
|
42
42
|
"@dhis2/prop-types": "^3.1.2",
|
|
43
|
-
"@dhis2/ui-constants": "9.9.0-alpha.
|
|
44
|
-
"@dhis2/ui-icons": "9.9.0-alpha.
|
|
43
|
+
"@dhis2/ui-constants": "9.9.0-alpha.4",
|
|
44
|
+
"@dhis2/ui-icons": "9.9.0-alpha.4",
|
|
45
45
|
"classnames": "^2.3.1",
|
|
46
46
|
"prop-types": "^15.7.2"
|
|
47
47
|
},
|