@instructure/ui-date-time-input 8.52.1-snapshot-7 → 8.53.0

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/CHANGELOG.md CHANGED
@@ -3,183 +3,98 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
- ## [8.52.1-snapshot-7](https://github.com/instructure/instructure-ui/compare/v8.52.0...v8.52.1-snapshot-7) (2024-02-07)
7
-
6
+ # [8.53.0](https://github.com/instructure/instructure-ui/compare/v8.52.0...v8.53.0) (2024-02-08)
8
7
 
9
8
  ### Features
10
9
 
11
- * **ui-date-time-input:** add placeholder to datetimeinput's timeselect ([3ec8cc3](https://github.com/instructure/instructure-ui/commit/3ec8cc31f06ee570b704f88b09739a0868da9ff7))
12
-
13
-
14
-
15
-
10
+ - **many:** provide defaults to dateInput for easier setup and use ([dcefc4e](https://github.com/instructure/instructure-ui/commit/dcefc4e259118264d76dfd248b01682a7c2ef661))
11
+ - **ui-date-time-input:** add initialTimeForNewDate prop to datetimeinput ([33711a3](https://github.com/instructure/instructure-ui/commit/33711a3d899c34913aafd40c7dec90faabc88cd0))
12
+ - **ui-date-time-input:** add placeholder to datetimeinput's timeselect ([3ec8cc3](https://github.com/instructure/instructure-ui/commit/3ec8cc31f06ee570b704f88b09739a0868da9ff7))
16
13
 
17
14
  # [8.52.0](https://github.com/instructure/instructure-ui/compare/v8.51.0...v8.52.0) (2024-02-02)
18
15
 
19
16
  **Note:** Version bump only for package @instructure/ui-date-time-input
20
17
 
21
-
22
-
23
-
24
-
25
18
  # [8.51.0](https://github.com/instructure/instructure-ui/compare/v8.50.0...v8.51.0) (2023-12-14)
26
19
 
27
-
28
20
  ### Features
29
21
 
30
- * **ui-date-time-input:** add showMessages prop ([5212a19](https://github.com/instructure/instructure-ui/commit/5212a192b56fed621c80a697579fc164f9592391))
31
-
32
-
33
-
34
-
22
+ - **ui-date-time-input:** add showMessages prop ([5212a19](https://github.com/instructure/instructure-ui/commit/5212a192b56fed621c80a697579fc164f9592391))
35
23
 
36
24
  # [8.50.0](https://github.com/instructure/instructure-ui/compare/v8.49.0...v8.50.0) (2023-12-05)
37
25
 
38
26
  **Note:** Version bump only for package @instructure/ui-date-time-input
39
27
 
40
-
41
-
42
-
43
-
44
28
  # [8.49.0](https://github.com/instructure/instructure-ui/compare/v8.48.3...v8.49.0) (2023-11-24)
45
29
 
46
30
  **Note:** Version bump only for package @instructure/ui-date-time-input
47
31
 
48
-
49
-
50
-
51
-
52
32
  ## [8.48.3](https://github.com/instructure/instructure-ui/compare/v8.48.2...v8.48.3) (2023-11-23)
53
33
 
54
34
  **Note:** Version bump only for package @instructure/ui-date-time-input
55
35
 
56
-
57
-
58
-
59
-
60
36
  ## [8.48.2](https://github.com/instructure/instructure-ui/compare/v8.48.1...v8.48.2) (2023-11-21)
61
37
 
62
38
  **Note:** Version bump only for package @instructure/ui-date-time-input
63
39
 
64
-
65
-
66
-
67
-
68
40
  ## [8.48.1](https://github.com/instructure/instructure-ui/compare/v8.48.0...v8.48.1) (2023-11-17)
69
41
 
70
42
  **Note:** Version bump only for package @instructure/ui-date-time-input
71
43
 
72
-
73
-
74
-
75
-
76
44
  # [8.48.0](https://github.com/instructure/instructure-ui/compare/v8.47.1...v8.48.0) (2023-11-10)
77
45
 
78
46
  **Note:** Version bump only for package @instructure/ui-date-time-input
79
47
 
80
-
81
-
82
-
83
-
84
48
  ## [8.47.1](https://github.com/instructure/instructure-ui/compare/v8.47.0...v8.47.1) (2023-11-06)
85
49
 
86
50
  **Note:** Version bump only for package @instructure/ui-date-time-input
87
51
 
88
-
89
-
90
-
91
-
92
52
  # [8.47.0](https://github.com/instructure/instructure-ui/compare/v8.46.1...v8.47.0) (2023-10-27)
93
53
 
94
-
95
54
  ### Bug Fixes
96
55
 
97
- * **ui-date-time-input:** clear TimeSelect value when DateInput value is cleared ([b1a58e5](https://github.com/instructure/instructure-ui/commit/b1a58e5c04822da491966bc0a79403b30749ff7e))
98
-
99
-
100
-
101
-
56
+ - **ui-date-time-input:** clear TimeSelect value when DateInput value is cleared ([b1a58e5](https://github.com/instructure/instructure-ui/commit/b1a58e5c04822da491966bc0a79403b30749ff7e))
102
57
 
103
58
  ## [8.46.1](https://github.com/instructure/instructure-ui/compare/v8.46.0...v8.46.1) (2023-10-13)
104
59
 
105
60
  **Note:** Version bump only for package @instructure/ui-date-time-input
106
61
 
107
-
108
-
109
-
110
-
111
62
  # [8.46.0](https://github.com/instructure/instructure-ui/compare/v8.45.0...v8.46.0) (2023-10-11)
112
63
 
113
64
  **Note:** Version bump only for package @instructure/ui-date-time-input
114
65
 
115
-
116
-
117
-
118
-
119
66
  # [8.45.0](https://github.com/instructure/instructure-ui/compare/v8.44.0...v8.45.0) (2023-10-03)
120
67
 
121
68
  **Note:** Version bump only for package @instructure/ui-date-time-input
122
69
 
123
-
124
-
125
-
126
-
127
70
  # [8.44.0](https://github.com/instructure/instructure-ui/compare/v8.43.1...v8.44.0) (2023-09-21)
128
71
 
129
72
  **Note:** Version bump only for package @instructure/ui-date-time-input
130
73
 
131
-
132
-
133
-
134
-
135
74
  ## [8.43.1](https://github.com/instructure/instructure-ui/compare/v8.43.0...v8.43.1) (2023-09-11)
136
75
 
137
76
  **Note:** Version bump only for package @instructure/ui-date-time-input
138
77
 
139
-
140
-
141
-
142
-
143
78
  # [8.43.0](https://github.com/instructure/instructure-ui/compare/v8.41.1...v8.43.0) (2023-09-07)
144
79
 
145
80
  **Note:** Version bump only for package @instructure/ui-date-time-input
146
81
 
147
-
148
-
149
-
150
-
151
82
  # [8.42.0](https://github.com/instructure/instructure-ui/compare/v8.41.1...v8.42.0) (2023-09-07)
152
83
 
153
84
  **Note:** Version bump only for package @instructure/ui-date-time-input
154
85
 
155
-
156
-
157
-
158
-
159
86
  ## [8.41.1](https://github.com/instructure/instructure-ui/compare/v8.41.0...v8.41.1) (2023-08-24)
160
87
 
161
88
  **Note:** Version bump only for package @instructure/ui-date-time-input
162
89
 
163
-
164
-
165
-
166
-
167
90
  # [8.41.0](https://github.com/instructure/instructure-ui/compare/v8.40.1...v8.41.0) (2023-08-21)
168
91
 
169
92
  **Note:** Version bump only for package @instructure/ui-date-time-input
170
93
 
171
-
172
-
173
-
174
-
175
94
  ## [8.40.1](https://github.com/instructure/instructure-ui/compare/v8.40.0...v8.40.1) (2023-08-18)
176
95
 
177
96
  **Note:** Version bump only for package @instructure/ui-date-time-input
178
97
 
179
-
180
-
181
-
182
-
183
98
  # [8.40.0](https://github.com/instructure/instructure-ui/compare/v8.39.0...v8.40.0) (2023-08-17)
184
99
 
185
100
  **Note:** Version bump only for package @instructure/ui-date-time-input
@@ -0,0 +1,167 @@
1
+ /*
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2018 - present Instructure, Inc.
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+
25
+ import React from 'react';
26
+ import { fireEvent, render, screen } from '@testing-library/react';
27
+ import '@testing-library/jest-dom';
28
+ import { userEvent } from '@testing-library/user-event';
29
+ import DateTimeInput from '../index';
30
+ describe('<DateTimeInput />', () => {
31
+ it("should change value of TimeSelect to initialTimeForNewDate prop's value", async () => {
32
+ const locale = 'en-US';
33
+ const timezone = 'US/Eastern';
34
+ render( /*#__PURE__*/React.createElement(DateTimeInput, {
35
+ description: "date time",
36
+ prevMonthLabel: "Previous month",
37
+ nextMonthLabel: "Next month",
38
+ dateRenderLabel: "date",
39
+ timeRenderLabel: "time",
40
+ invalidDateTimeMessage: "whoops",
41
+ locale: locale,
42
+ timezone: timezone,
43
+ initialTimeForNewDate: "05:05"
44
+ }));
45
+ const input = screen.getAllByRole('combobox')[0];
46
+ fireEvent.click(input);
47
+ const firstDay = screen.getByText('15');
48
+ await userEvent.click(firstDay);
49
+ const allInputs = screen.getAllByRole('combobox');
50
+ const targetInput = allInputs.find(input => input.value === '5:05 AM');
51
+ expect(targetInput).toBeInTheDocument();
52
+ });
53
+ it("should throw warning if initialTimeForNewDate prop's value is not HH:MM", async () => {
54
+ const locale = 'en-US';
55
+ const timezone = 'US/Eastern';
56
+ const consoleError = jest.spyOn(console, 'error').mockImplementation(() => {});
57
+ const initialTimeForNewDate = 'WRONG_FORMAT';
58
+ render( /*#__PURE__*/React.createElement(DateTimeInput, {
59
+ description: "date time",
60
+ prevMonthLabel: "Previous month",
61
+ nextMonthLabel: "Next month",
62
+ dateRenderLabel: "date",
63
+ timeRenderLabel: "time",
64
+ invalidDateTimeMessage: "whoops",
65
+ locale: locale,
66
+ timezone: timezone,
67
+ initialTimeForNewDate: initialTimeForNewDate
68
+ }));
69
+ expect(consoleError.mock.calls[0][2]).toContain(`Invalid prop \`initialTimeForNewDate\` \`${initialTimeForNewDate}\` supplied to \`DateTimeInput\`, expected a HH:MM formatted string.`);
70
+ const input = screen.getAllByRole('combobox')[0];
71
+ fireEvent.click(input);
72
+ const firstDay = screen.getByText('15');
73
+ await userEvent.click(firstDay);
74
+ expect(consoleError.mock.calls[1][0]).toBe(`Warning: [DateTimeInput] initialTimeForNewDate prop is not in the correct format. Please use HH:MM format.`);
75
+ });
76
+ it('should throw warning if initialTimeForNewDate prop hour and minute values are not in interval', async () => {
77
+ const locale = 'en-US';
78
+ const timezone = 'US/Eastern';
79
+ const consoleError = jest.spyOn(console, 'error').mockImplementation(() => {});
80
+ const initialTimeForNewDate = '99:99';
81
+ render( /*#__PURE__*/React.createElement(DateTimeInput, {
82
+ description: "date time",
83
+ prevMonthLabel: "Previous month",
84
+ nextMonthLabel: "Next month",
85
+ dateRenderLabel: "date",
86
+ timeRenderLabel: "time",
87
+ invalidDateTimeMessage: "whoops",
88
+ locale: locale,
89
+ timezone: timezone,
90
+ initialTimeForNewDate: initialTimeForNewDate
91
+ }));
92
+ const input = screen.getAllByRole('combobox')[0];
93
+ fireEvent.click(input);
94
+ const firstDay = screen.getByText('15');
95
+ await userEvent.click(firstDay);
96
+ expect(consoleError.mock.calls[0][0]).toContain(`Warning: [DateTimeInput] 0 <= hour < 24 and 0 <= minute < 60 for initialTimeForNewDate prop.`);
97
+ });
98
+
99
+ /*
100
+ * TODO write this test with Cypress
101
+ * Steps:
102
+ * 0. Set initialTimeForNewDate and defaultValue props -> check if defaultValue is rendered in the date input and time input fields
103
+ * 1. Clear date input field
104
+ * 2. Click on an area outside of the component
105
+ * 3. Check if time input field got cleared as well
106
+ * 4. Click on the date input and select a date
107
+ * 5. Observe if the time input has the value of initialTimeForNewDate
108
+ */
109
+
110
+ // it('should merge defaultValue and initialTimeForNewDate when the user clears date input', async () => {
111
+ // const locale = 'en-US'
112
+ // const timezone = 'US/Eastern'
113
+ //
114
+ // // const consoleWarn = jest.spyOn(console, 'warn').mockImplementation(() => {})
115
+ //
116
+ // const initialTimeForNewDate = '16:16'
117
+ // const defaultValue = '2018-01-18T13:30'
118
+ //
119
+ // const {container} = render(
120
+ // <DateTimeInput
121
+ // description="date time"
122
+ // prevMonthLabel="Previous month"
123
+ // nextMonthLabel="Next month"
124
+ // dateRenderLabel="date"
125
+ // timeRenderLabel="time"
126
+ // invalidDateTimeMessage="whoops"
127
+ // defaultValue={defaultValue}
128
+ // locale={locale}
129
+ // timezone={timezone}
130
+ // initialTimeForNewDate={initialTimeForNewDate}
131
+ // />
132
+ // )
133
+ //
134
+ // const dateInput = container.querySelector('input')
135
+ //
136
+ // const timeInput = container.querySelectorAll('input')[1]
137
+ //
138
+ // fireEvent.change(dateInput as Element, { target: { value: '' } })
139
+ //
140
+ //
141
+ // await waitFor(() => {
142
+ // fireEvent.keyDown(dateInput as Element, { key: 'Escape' })
143
+ //
144
+ // expect(timeInput).toHaveValue('')
145
+ // })
146
+ //
147
+ // // fireEvent.
148
+ // // fireEvent.change(timeInput as Element, { target: { value: '' } })
149
+ //
150
+ //
151
+ // const firstDay = screen.getByText('15')
152
+ //
153
+ // await userEvent.click(firstDay)
154
+ //
155
+ // const time = screen.getByText('4:16 PM')
156
+ //
157
+ // expect(time).toBeInTheDocument()
158
+ //
159
+ // // expect(consoleWarn.mock.calls[0][0]).toContain(
160
+ // // `Warning: [DateTimeInput] if defaultValue is set, initialTimeForNewDate will be ignored.`
161
+ // // )
162
+ // })
163
+
164
+ afterEach(() => {
165
+ jest.resetAllMocks();
166
+ });
167
+ });
@@ -34,6 +34,7 @@ import { AccessibleContent } from '@instructure/ui-a11y-content';
34
34
  import { IconButton } from '@instructure/ui-buttons';
35
35
  import { IconArrowOpenEndSolid, IconArrowOpenStartSolid } from '@instructure/ui-icons';
36
36
  import { propTypes, allowedProps } from './props';
37
+ import { error } from '@instructure/console';
37
38
 
38
39
  /**
39
40
  ---
@@ -209,6 +210,7 @@ let DateTimeInput = (_dec = testable(), _dec(_class = (_class2 = class DateTimeI
209
210
  if (dateStr) {
210
211
  const parsed = DateTime.parse(dateStr, this.locale(), this.timezone());
211
212
  if (parsed.isValid()) {
213
+ var _this$state;
212
214
  if (doNotChangeTime && this.state.timeSelectValue) {
213
215
  // There is a selected time, adjust the parsed date to its value
214
216
  const timeParsed = DateTime.parse(this.state.timeSelectValue, this.locale(), this.timezone());
@@ -217,6 +219,17 @@ let DateTimeInput = (_dec = testable(), _dec(_class = (_class2 = class DateTimeI
217
219
  if (doNotChangeDate && this.state.iso) {
218
220
  parsed.date(this.state.iso.date()).month(this.state.iso.month()).year(this.state.iso.year());
219
221
  }
222
+ if (this.props.initialTimeForNewDate && !((_this$state = this.state) !== null && _this$state !== void 0 && _this$state.timeSelectValue)) {
223
+ const hour = Number(this.props.initialTimeForNewDate.slice(0, 2));
224
+ const minute = Number(this.props.initialTimeForNewDate.slice(3, 5));
225
+ if (isNaN(hour) || isNaN(minute)) {
226
+ error(false, `[DateTimeInput] initialTimeForNewDate prop is not in the correct format. Please use HH:MM format.`);
227
+ } else if (hour < 0 || hour > 23 || minute > 59 || minute < 0) {
228
+ error(false, `[DateTimeInput] 0 <= hour < 24 and 0 <= minute < 60 for initialTimeForNewDate prop.`);
229
+ } else {
230
+ parsed.hour(hour).minute(minute);
231
+ }
232
+ }
220
233
  const newTimeSelectValue = parsed.toISOString();
221
234
  if (this.isDisabledDate(parsed)) {
222
235
  let text = typeof this.props.disabledDateTimeMessage === 'function' ? this.props.disabledDateTimeMessage(parsed.toISOString(true)) : this.props.disabledDateTimeMessage;
@@ -322,10 +335,6 @@ let DateTimeInput = (_dec = testable(), _dec(_class = (_class2 = class DateTimeI
322
335
  this.changeStateIfNeeded(newState, event);
323
336
  }
324
337
  renderDays() {
325
- if (!this.state.isShowingCalendar) {
326
- // this is an expensive function, only execute if the calendar is open
327
- return;
328
- }
329
338
  const renderedDate = this.state.renderedDate;
330
339
  // Sets it to the first local day of the week counting back from the start of the month.
331
340
  // Note that first day depends on the locale, e.g. it's Sunday in the US and
@@ -26,6 +26,15 @@ import { FormPropTypes } from '@instructure/ui-form-field';
26
26
  import { I18nPropTypes } from '@instructure/ui-i18n';
27
27
  import PropTypes from 'prop-types';
28
28
  import { controllable } from '@instructure/ui-prop-types';
29
+ const hourMinuteValidator = function (props, propName, componentName, location) {
30
+ const propValue = props[propName];
31
+ if (typeof propValue === 'undefined' || propValue === '') return null;
32
+ const hourMinuteRegex = /^\d{2}:\d{2}$/;
33
+ if (typeof propValue === 'string' && !propValue.match(hourMinuteRegex)) {
34
+ return new Error(`Invalid ${location} \`${propName}\` \`${propValue}\` supplied to \`${componentName}\`, expected ` + `a HH:MM formatted string.`);
35
+ }
36
+ return null;
37
+ };
29
38
  const propTypes = {
30
39
  description: PropTypes.node.isRequired,
31
40
  dateRenderLabel: PropTypes.oneOfType([PropTypes.func, PropTypes.node]).isRequired,
@@ -57,7 +66,8 @@ const propTypes = {
57
66
  onBlur: PropTypes.func,
58
67
  disabledDates: PropTypes.oneOfType([PropTypes.func, PropTypes.arrayOf(PropTypes.string)]),
59
68
  disabledDateTimeMessage: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
60
- allowNonStepInput: PropTypes.bool
69
+ allowNonStepInput: PropTypes.bool,
70
+ initialTimeForNewDate: hourMinuteValidator
61
71
  };
62
72
  const allowedProps = ['description', 'dateRenderLabel', 'prevMonthLabel', 'nextMonthLabel', 'datePlaceholder', 'timePlaceholder', 'dateFormat', 'interaction', 'timeRenderLabel', 'timeStep', 'timeFormat', 'locale', 'timezone', 'invalidDateTimeMessage', 'showMessages', 'messages', 'messageFormat', 'layout', 'rowSpacing', 'colSpacing', 'value', 'defaultValue', 'renderWeekdayLabels', 'isRequired', 'onChange', 'dateInputRef', 'timeInputRef', 'onBlur', 'disabledDates', 'disabledDateTimeMessage', 'allowNonStepInput'];
63
73
  export { propTypes, allowedProps };
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
+ var _react = _interopRequireDefault(require("react"));
5
+ var _react2 = require("@testing-library/react");
6
+ require("@testing-library/jest-dom");
7
+ var _userEvent = require("@testing-library/user-event");
8
+ var _index = _interopRequireDefault(require("../index"));
9
+ /*
10
+ * The MIT License (MIT)
11
+ *
12
+ * Copyright (c) 2018 - present Instructure, Inc.
13
+ *
14
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
15
+ * of this software and associated documentation files (the "Software"), to deal
16
+ * in the Software without restriction, including without limitation the rights
17
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
+ * copies of the Software, and to permit persons to whom the Software is
19
+ * furnished to do so, subject to the following conditions:
20
+ *
21
+ * The above copyright notice and this permission notice shall be included in all
22
+ * copies or substantial portions of the Software.
23
+ *
24
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30
+ * SOFTWARE.
31
+ */
32
+
33
+ describe('<DateTimeInput />', () => {
34
+ it("should change value of TimeSelect to initialTimeForNewDate prop's value", async () => {
35
+ const locale = 'en-US';
36
+ const timezone = 'US/Eastern';
37
+ (0, _react2.render)( /*#__PURE__*/_react.default.createElement(_index.default, {
38
+ description: "date time",
39
+ prevMonthLabel: "Previous month",
40
+ nextMonthLabel: "Next month",
41
+ dateRenderLabel: "date",
42
+ timeRenderLabel: "time",
43
+ invalidDateTimeMessage: "whoops",
44
+ locale: locale,
45
+ timezone: timezone,
46
+ initialTimeForNewDate: "05:05"
47
+ }));
48
+ const input = _react2.screen.getAllByRole('combobox')[0];
49
+ _react2.fireEvent.click(input);
50
+ const firstDay = _react2.screen.getByText('15');
51
+ await _userEvent.userEvent.click(firstDay);
52
+ const allInputs = _react2.screen.getAllByRole('combobox');
53
+ const targetInput = allInputs.find(input => input.value === '5:05 AM');
54
+ expect(targetInput).toBeInTheDocument();
55
+ });
56
+ it("should throw warning if initialTimeForNewDate prop's value is not HH:MM", async () => {
57
+ const locale = 'en-US';
58
+ const timezone = 'US/Eastern';
59
+ const consoleError = jest.spyOn(console, 'error').mockImplementation(() => {});
60
+ const initialTimeForNewDate = 'WRONG_FORMAT';
61
+ (0, _react2.render)( /*#__PURE__*/_react.default.createElement(_index.default, {
62
+ description: "date time",
63
+ prevMonthLabel: "Previous month",
64
+ nextMonthLabel: "Next month",
65
+ dateRenderLabel: "date",
66
+ timeRenderLabel: "time",
67
+ invalidDateTimeMessage: "whoops",
68
+ locale: locale,
69
+ timezone: timezone,
70
+ initialTimeForNewDate: initialTimeForNewDate
71
+ }));
72
+ expect(consoleError.mock.calls[0][2]).toContain(`Invalid prop \`initialTimeForNewDate\` \`${initialTimeForNewDate}\` supplied to \`DateTimeInput\`, expected a HH:MM formatted string.`);
73
+ const input = _react2.screen.getAllByRole('combobox')[0];
74
+ _react2.fireEvent.click(input);
75
+ const firstDay = _react2.screen.getByText('15');
76
+ await _userEvent.userEvent.click(firstDay);
77
+ expect(consoleError.mock.calls[1][0]).toBe(`Warning: [DateTimeInput] initialTimeForNewDate prop is not in the correct format. Please use HH:MM format.`);
78
+ });
79
+ it('should throw warning if initialTimeForNewDate prop hour and minute values are not in interval', async () => {
80
+ const locale = 'en-US';
81
+ const timezone = 'US/Eastern';
82
+ const consoleError = jest.spyOn(console, 'error').mockImplementation(() => {});
83
+ const initialTimeForNewDate = '99:99';
84
+ (0, _react2.render)( /*#__PURE__*/_react.default.createElement(_index.default, {
85
+ description: "date time",
86
+ prevMonthLabel: "Previous month",
87
+ nextMonthLabel: "Next month",
88
+ dateRenderLabel: "date",
89
+ timeRenderLabel: "time",
90
+ invalidDateTimeMessage: "whoops",
91
+ locale: locale,
92
+ timezone: timezone,
93
+ initialTimeForNewDate: initialTimeForNewDate
94
+ }));
95
+ const input = _react2.screen.getAllByRole('combobox')[0];
96
+ _react2.fireEvent.click(input);
97
+ const firstDay = _react2.screen.getByText('15');
98
+ await _userEvent.userEvent.click(firstDay);
99
+ expect(consoleError.mock.calls[0][0]).toContain(`Warning: [DateTimeInput] 0 <= hour < 24 and 0 <= minute < 60 for initialTimeForNewDate prop.`);
100
+ });
101
+
102
+ /*
103
+ * TODO write this test with Cypress
104
+ * Steps:
105
+ * 0. Set initialTimeForNewDate and defaultValue props -> check if defaultValue is rendered in the date input and time input fields
106
+ * 1. Clear date input field
107
+ * 2. Click on an area outside of the component
108
+ * 3. Check if time input field got cleared as well
109
+ * 4. Click on the date input and select a date
110
+ * 5. Observe if the time input has the value of initialTimeForNewDate
111
+ */
112
+
113
+ // it('should merge defaultValue and initialTimeForNewDate when the user clears date input', async () => {
114
+ // const locale = 'en-US'
115
+ // const timezone = 'US/Eastern'
116
+ //
117
+ // // const consoleWarn = jest.spyOn(console, 'warn').mockImplementation(() => {})
118
+ //
119
+ // const initialTimeForNewDate = '16:16'
120
+ // const defaultValue = '2018-01-18T13:30'
121
+ //
122
+ // const {container} = render(
123
+ // <DateTimeInput
124
+ // description="date time"
125
+ // prevMonthLabel="Previous month"
126
+ // nextMonthLabel="Next month"
127
+ // dateRenderLabel="date"
128
+ // timeRenderLabel="time"
129
+ // invalidDateTimeMessage="whoops"
130
+ // defaultValue={defaultValue}
131
+ // locale={locale}
132
+ // timezone={timezone}
133
+ // initialTimeForNewDate={initialTimeForNewDate}
134
+ // />
135
+ // )
136
+ //
137
+ // const dateInput = container.querySelector('input')
138
+ //
139
+ // const timeInput = container.querySelectorAll('input')[1]
140
+ //
141
+ // fireEvent.change(dateInput as Element, { target: { value: '' } })
142
+ //
143
+ //
144
+ // await waitFor(() => {
145
+ // fireEvent.keyDown(dateInput as Element, { key: 'Escape' })
146
+ //
147
+ // expect(timeInput).toHaveValue('')
148
+ // })
149
+ //
150
+ // // fireEvent.
151
+ // // fireEvent.change(timeInput as Element, { target: { value: '' } })
152
+ //
153
+ //
154
+ // const firstDay = screen.getByText('15')
155
+ //
156
+ // await userEvent.click(firstDay)
157
+ //
158
+ // const time = screen.getByText('4:16 PM')
159
+ //
160
+ // expect(time).toBeInTheDocument()
161
+ //
162
+ // // expect(consoleWarn.mock.calls[0][0]).toContain(
163
+ // // `Warning: [DateTimeInput] if defaultValue is set, initialTimeForNewDate will be ignored.`
164
+ // // )
165
+ // })
166
+
167
+ afterEach(() => {
168
+ jest.resetAllMocks();
169
+ });
170
+ });
@@ -19,6 +19,7 @@ var _IconButton = require("@instructure/ui-buttons/lib/IconButton");
19
19
  var _IconArrowOpenEndSolid = require("@instructure/ui-icons/lib/IconArrowOpenEndSolid.js");
20
20
  var _IconArrowOpenStartSolid = require("@instructure/ui-icons/lib/IconArrowOpenStartSolid.js");
21
21
  var _props = require("./props");
22
+ var _console = require("@instructure/console");
22
23
  var _dec, _class, _class2, _IconArrowOpenStartSo, _IconArrowOpenEndSoli;
23
24
  /*
24
25
  * The MIT License (MIT)
@@ -217,6 +218,7 @@ let DateTimeInput = exports.DateTimeInput = (_dec = (0, _testable.testable)(), _
217
218
  if (dateStr) {
218
219
  const parsed = _DateTime.DateTime.parse(dateStr, this.locale(), this.timezone());
219
220
  if (parsed.isValid()) {
221
+ var _this$state;
220
222
  if (doNotChangeTime && this.state.timeSelectValue) {
221
223
  // There is a selected time, adjust the parsed date to its value
222
224
  const timeParsed = _DateTime.DateTime.parse(this.state.timeSelectValue, this.locale(), this.timezone());
@@ -225,6 +227,17 @@ let DateTimeInput = exports.DateTimeInput = (_dec = (0, _testable.testable)(), _
225
227
  if (doNotChangeDate && this.state.iso) {
226
228
  parsed.date(this.state.iso.date()).month(this.state.iso.month()).year(this.state.iso.year());
227
229
  }
230
+ if (this.props.initialTimeForNewDate && !((_this$state = this.state) !== null && _this$state !== void 0 && _this$state.timeSelectValue)) {
231
+ const hour = Number(this.props.initialTimeForNewDate.slice(0, 2));
232
+ const minute = Number(this.props.initialTimeForNewDate.slice(3, 5));
233
+ if (isNaN(hour) || isNaN(minute)) {
234
+ (0, _console.error)(false, `[DateTimeInput] initialTimeForNewDate prop is not in the correct format. Please use HH:MM format.`);
235
+ } else if (hour < 0 || hour > 23 || minute > 59 || minute < 0) {
236
+ (0, _console.error)(false, `[DateTimeInput] 0 <= hour < 24 and 0 <= minute < 60 for initialTimeForNewDate prop.`);
237
+ } else {
238
+ parsed.hour(hour).minute(minute);
239
+ }
240
+ }
228
241
  const newTimeSelectValue = parsed.toISOString();
229
242
  if (this.isDisabledDate(parsed)) {
230
243
  let text = typeof this.props.disabledDateTimeMessage === 'function' ? this.props.disabledDateTimeMessage(parsed.toISOString(true)) : this.props.disabledDateTimeMessage;
@@ -330,10 +343,6 @@ let DateTimeInput = exports.DateTimeInput = (_dec = (0, _testable.testable)(), _
330
343
  this.changeStateIfNeeded(newState, event);
331
344
  }
332
345
  renderDays() {
333
- if (!this.state.isShowingCalendar) {
334
- // this is an expensive function, only execute if the calendar is open
335
- return;
336
- }
337
346
  const renderedDate = this.state.renderedDate;
338
347
  // Sets it to the first local day of the week counting back from the start of the month.
339
348
  // Note that first day depends on the locale, e.g. it's Sunday in the US and
@@ -33,6 +33,15 @@ var _controllable = require("@instructure/ui-prop-types/lib/controllable.js");
33
33
  * SOFTWARE.
34
34
  */
35
35
 
36
+ const hourMinuteValidator = function (props, propName, componentName, location) {
37
+ const propValue = props[propName];
38
+ if (typeof propValue === 'undefined' || propValue === '') return null;
39
+ const hourMinuteRegex = /^\d{2}:\d{2}$/;
40
+ if (typeof propValue === 'string' && !propValue.match(hourMinuteRegex)) {
41
+ return new Error(`Invalid ${location} \`${propName}\` \`${propValue}\` supplied to \`${componentName}\`, expected ` + `a HH:MM formatted string.`);
42
+ }
43
+ return null;
44
+ };
36
45
  const propTypes = exports.propTypes = {
37
46
  description: _propTypes.default.node.isRequired,
38
47
  dateRenderLabel: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.node]).isRequired,
@@ -64,6 +73,7 @@ const propTypes = exports.propTypes = {
64
73
  onBlur: _propTypes.default.func,
65
74
  disabledDates: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.arrayOf(_propTypes.default.string)]),
66
75
  disabledDateTimeMessage: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.func]),
67
- allowNonStepInput: _propTypes.default.bool
76
+ allowNonStepInput: _propTypes.default.bool,
77
+ initialTimeForNewDate: hourMinuteValidator
68
78
  };
69
79
  const allowedProps = exports.allowedProps = ['description', 'dateRenderLabel', 'prevMonthLabel', 'nextMonthLabel', 'datePlaceholder', 'timePlaceholder', 'dateFormat', 'interaction', 'timeRenderLabel', 'timeStep', 'timeFormat', 'locale', 'timezone', 'invalidDateTimeMessage', 'showMessages', 'messages', 'messageFormat', 'layout', 'rowSpacing', 'colSpacing', 'value', 'defaultValue', 'renderWeekdayLabels', 'isRequired', 'onChange', 'dateInputRef', 'timeInputRef', 'onBlur', 'disabledDates', 'disabledDateTimeMessage', 'allowNonStepInput'];