@instructure/ui-date-time-input 10.9.1-snapshot-1 → 10.9.1-snapshot-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/CHANGELOG.md CHANGED
@@ -3,9 +3,13 @@
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
- ## [10.9.1-snapshot-1](https://github.com/instructure/instructure-ui/compare/v10.9.0...v10.9.1-snapshot-1) (2024-12-13)
6
+ ## [10.9.1-snapshot-4](https://github.com/instructure/instructure-ui/compare/v10.9.0...v10.9.1-snapshot-4) (2024-12-18)
7
7
 
8
- **Note:** Version bump only for package @instructure/ui-date-time-input
8
+
9
+ ### Bug Fixes
10
+
11
+ * **ui-date-time-input,ui-form-field:** make DateTimeInput compatible with the new error format ([07c9443](https://github.com/instructure/instructure-ui/commit/07c9443116dc53b1d2da317e42f314ce3e9b9c39))
12
+ * **ui-date-time-input:** fix DateTimeInput displaying wrong value of its value is changed in a onChange callback ([69e9d24](https://github.com/instructure/instructure-ui/commit/69e9d2458b520b83eefb131d8de9a9a658f627a0))
9
13
 
10
14
 
11
15
 
@@ -143,7 +143,7 @@ describe('<DateTimeInput />', () => {
143
143
  invalidDateTimeMessage: invalidDateTimeMessage
144
144
  })),
145
145
  container = _render.container;
146
- const timeInput = screen.getByLabelText('time-input');
146
+ const timeInput = screen.getByLabelText('time-input *');
147
147
  await userEvent.type(timeInput, '1:00 PM');
148
148
  fireEvent.blur(timeInput);
149
149
  await waitFor(() => {
@@ -75,7 +75,7 @@ let DateTimeInput = (_dec = testable(), _dec(_class = (_DateTimeInput = class Da
75
75
  event.persist();
76
76
  // timeout is needed here because handleDayClick could be called in the same
77
77
  // frame, and it updates calendarSelectedDate which is read in here.
78
- window.setTimeout(() => {
78
+ setTimeout(() => {
79
79
  if (event.key === 'Enter') {
80
80
  // user pressed enter, use the selected value in the calendar
81
81
  this.updateStateBasedOnDateInput(this.state.calendarSelectedDate, event);
@@ -108,12 +108,20 @@ let DateTimeInput = (_dec = testable(), _dec(_class = (_DateTimeInput = class Da
108
108
  // eslint-disable-next-line no-param-reassign
109
109
  newState.message = {
110
110
  text: text,
111
- type: 'error'
111
+ type: 'newError'
112
112
  };
113
113
  }
114
114
  if (this.areDifferentDates(this.state.iso, newState.iso)) {
115
- var _this$props$onChange, _this$props, _newState$iso;
116
- (_this$props$onChange = (_this$props = this.props).onChange) === null || _this$props$onChange === void 0 ? void 0 : _this$props$onChange.call(_this$props, e, (_newState$iso = newState.iso) === null || _newState$iso === void 0 ? void 0 : _newState$iso.toISOString());
115
+ if (typeof this.props.onChange === 'function') {
116
+ var _newState$iso;
117
+ const newDate = (_newState$iso = newState.iso) === null || _newState$iso === void 0 ? void 0 : _newState$iso.toISOString();
118
+ // Timeout is needed here because users might change value in the
119
+ // onChange event lister, which might not execute properly
120
+ setTimeout(() => {
121
+ var _this$props$onChange, _this$props;
122
+ (_this$props$onChange = (_this$props = this.props).onChange) === null || _this$props$onChange === void 0 ? void 0 : _this$props$onChange.call(_this$props, e, newDate);
123
+ }, 0);
124
+ }
117
125
  }
118
126
  this.setState(newState);
119
127
  };
@@ -128,7 +136,7 @@ let DateTimeInput = (_dec = testable(), _dec(_class = (_DateTimeInput = class Da
128
136
  // happens on the target before the relatedTarget gets focus.
129
137
  // The timeout gives it a moment for that to happen
130
138
  if (typeof this.props.onBlur === 'function') {
131
- window.setTimeout(() => {
139
+ setTimeout(() => {
132
140
  var _this$props$onBlur, _this$props2;
133
141
  (_this$props$onBlur = (_this$props2 = this.props).onBlur) === null || _this$props$onBlur === void 0 ? void 0 : _this$props$onBlur.call(_this$props2, e);
134
142
  }, 0);
@@ -241,7 +249,7 @@ let DateTimeInput = (_dec = testable(), _dec(_class = (_DateTimeInput = class Da
241
249
  }
242
250
  errorMsg = text ? {
243
251
  text,
244
- type: 'error'
252
+ type: 'newError'
245
253
  } : void 0;
246
254
  return {
247
255
  iso: parsed.clone(),
@@ -449,6 +457,13 @@ let DateTimeInput = (_dec = testable(), _dec(_class = (_DateTimeInput = class Da
449
457
  interaction = _this$props4.interaction,
450
458
  renderWeekdayLabels = _this$props4.renderWeekdayLabels,
451
459
  allowNonStepInput = _this$props4.allowNonStepInput;
460
+ const allMessages = [...(showMessages && this.state.message ? [this.state.message] : []), ...(messages || [])];
461
+ const hasError = allMessages.find(m => m.type === 'newError');
462
+ // if the component is in error state, create an empty error message to pass down to the subcomponents (DateInput and TimeInput) so they get a red outline and red required asterisk
463
+ const subComponentMessages = hasError ? [{
464
+ type: 'newError',
465
+ text: ''
466
+ }] : [];
452
467
  return /*#__PURE__*/React.createElement(FormFieldGroup, {
453
468
  description: description,
454
469
  layout: layout,
@@ -456,6 +471,7 @@ let DateTimeInput = (_dec = testable(), _dec(_class = (_DateTimeInput = class Da
456
471
  colSpacing: colSpacing,
457
472
  vAlign: "top",
458
473
  elementRef: this.handleRef,
474
+ isGroup: false,
459
475
  messages: [...(showMessages && this.state.message ? [this.state.message] : []), ...(messages || [])]
460
476
  }, /*#__PURE__*/React.createElement(DateInput, {
461
477
  display: "block",
@@ -476,6 +492,7 @@ let DateTimeInput = (_dec = testable(), _dec(_class = (_DateTimeInput = class Da
476
492
  onRequestRenderNextMonth: this.handleRenderNextMonth,
477
493
  onRequestRenderPrevMonth: this.handleRenderPrevMonth,
478
494
  isRequired: isRequired,
495
+ messages: subComponentMessages,
479
496
  interaction: interaction,
480
497
  renderNavigationLabel: /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement("div", null, this.state.renderedDate.format('MMMM')), /*#__PURE__*/React.createElement("div", null, this.state.renderedDate.format('y')))
481
498
  }, this.renderDays()), /*#__PURE__*/React.createElement(TimeSelect, {
@@ -490,7 +507,9 @@ let DateTimeInput = (_dec = testable(), _dec(_class = (_DateTimeInput = class Da
490
507
  timezone: timezone,
491
508
  inputRef: timeInputRef,
492
509
  interaction: interaction,
493
- allowNonStepInput: allowNonStepInput
510
+ allowNonStepInput: allowNonStepInput,
511
+ isRequired: isRequired,
512
+ messages: subComponentMessages
494
513
  }));
495
514
  }
496
515
  }, _DateTimeInput.displayName = "DateTimeInput", _DateTimeInput.DEFAULT_MESSAGE_FORMAT = 'LLLL', _DateTimeInput.propTypes = propTypes, _DateTimeInput.allowedProps = allowedProps, _DateTimeInput.defaultProps = {
@@ -146,7 +146,7 @@ describe('<DateTimeInput />', () => {
146
146
  invalidDateTimeMessage: invalidDateTimeMessage
147
147
  })),
148
148
  container = _render.container;
149
- const timeInput = _react2.screen.getByLabelText('time-input');
149
+ const timeInput = _react2.screen.getByLabelText('time-input *');
150
150
  await _userEvent.default.type(timeInput, '1:00 PM');
151
151
  _react2.fireEvent.blur(timeInput);
152
152
  await (0, _react2.waitFor)(() => {
@@ -83,7 +83,7 @@ let DateTimeInput = exports.DateTimeInput = (_dec = (0, _testable.testable)(), _
83
83
  event.persist();
84
84
  // timeout is needed here because handleDayClick could be called in the same
85
85
  // frame, and it updates calendarSelectedDate which is read in here.
86
- window.setTimeout(() => {
86
+ setTimeout(() => {
87
87
  if (event.key === 'Enter') {
88
88
  // user pressed enter, use the selected value in the calendar
89
89
  this.updateStateBasedOnDateInput(this.state.calendarSelectedDate, event);
@@ -116,12 +116,20 @@ let DateTimeInput = exports.DateTimeInput = (_dec = (0, _testable.testable)(), _
116
116
  // eslint-disable-next-line no-param-reassign
117
117
  newState.message = {
118
118
  text: text,
119
- type: 'error'
119
+ type: 'newError'
120
120
  };
121
121
  }
122
122
  if (this.areDifferentDates(this.state.iso, newState.iso)) {
123
- var _this$props$onChange, _this$props, _newState$iso;
124
- (_this$props$onChange = (_this$props = this.props).onChange) === null || _this$props$onChange === void 0 ? void 0 : _this$props$onChange.call(_this$props, e, (_newState$iso = newState.iso) === null || _newState$iso === void 0 ? void 0 : _newState$iso.toISOString());
123
+ if (typeof this.props.onChange === 'function') {
124
+ var _newState$iso;
125
+ const newDate = (_newState$iso = newState.iso) === null || _newState$iso === void 0 ? void 0 : _newState$iso.toISOString();
126
+ // Timeout is needed here because users might change value in the
127
+ // onChange event lister, which might not execute properly
128
+ setTimeout(() => {
129
+ var _this$props$onChange, _this$props;
130
+ (_this$props$onChange = (_this$props = this.props).onChange) === null || _this$props$onChange === void 0 ? void 0 : _this$props$onChange.call(_this$props, e, newDate);
131
+ }, 0);
132
+ }
125
133
  }
126
134
  this.setState(newState);
127
135
  };
@@ -136,7 +144,7 @@ let DateTimeInput = exports.DateTimeInput = (_dec = (0, _testable.testable)(), _
136
144
  // happens on the target before the relatedTarget gets focus.
137
145
  // The timeout gives it a moment for that to happen
138
146
  if (typeof this.props.onBlur === 'function') {
139
- window.setTimeout(() => {
147
+ setTimeout(() => {
140
148
  var _this$props$onBlur, _this$props2;
141
149
  (_this$props$onBlur = (_this$props2 = this.props).onBlur) === null || _this$props$onBlur === void 0 ? void 0 : _this$props$onBlur.call(_this$props2, e);
142
150
  }, 0);
@@ -249,7 +257,7 @@ let DateTimeInput = exports.DateTimeInput = (_dec = (0, _testable.testable)(), _
249
257
  }
250
258
  errorMsg = text ? {
251
259
  text,
252
- type: 'error'
260
+ type: 'newError'
253
261
  } : void 0;
254
262
  return {
255
263
  iso: parsed.clone(),
@@ -457,6 +465,13 @@ let DateTimeInput = exports.DateTimeInput = (_dec = (0, _testable.testable)(), _
457
465
  interaction = _this$props4.interaction,
458
466
  renderWeekdayLabels = _this$props4.renderWeekdayLabels,
459
467
  allowNonStepInput = _this$props4.allowNonStepInput;
468
+ const allMessages = [...(showMessages && this.state.message ? [this.state.message] : []), ...(messages || [])];
469
+ const hasError = allMessages.find(m => m.type === 'newError');
470
+ // if the component is in error state, create an empty error message to pass down to the subcomponents (DateInput and TimeInput) so they get a red outline and red required asterisk
471
+ const subComponentMessages = hasError ? [{
472
+ type: 'newError',
473
+ text: ''
474
+ }] : [];
460
475
  return /*#__PURE__*/_react.default.createElement(_FormFieldGroup.FormFieldGroup, {
461
476
  description: description,
462
477
  layout: layout,
@@ -464,6 +479,7 @@ let DateTimeInput = exports.DateTimeInput = (_dec = (0, _testable.testable)(), _
464
479
  colSpacing: colSpacing,
465
480
  vAlign: "top",
466
481
  elementRef: this.handleRef,
482
+ isGroup: false,
467
483
  messages: [...(showMessages && this.state.message ? [this.state.message] : []), ...(messages || [])]
468
484
  }, /*#__PURE__*/_react.default.createElement(_DateInput.DateInput, {
469
485
  display: "block",
@@ -484,6 +500,7 @@ let DateTimeInput = exports.DateTimeInput = (_dec = (0, _testable.testable)(), _
484
500
  onRequestRenderNextMonth: this.handleRenderNextMonth,
485
501
  onRequestRenderPrevMonth: this.handleRenderPrevMonth,
486
502
  isRequired: isRequired,
503
+ messages: subComponentMessages,
487
504
  interaction: interaction,
488
505
  renderNavigationLabel: /*#__PURE__*/_react.default.createElement("span", null, /*#__PURE__*/_react.default.createElement("div", null, this.state.renderedDate.format('MMMM')), /*#__PURE__*/_react.default.createElement("div", null, this.state.renderedDate.format('y')))
489
506
  }, this.renderDays()), /*#__PURE__*/_react.default.createElement(_TimeSelect.TimeSelect, {
@@ -498,7 +515,9 @@ let DateTimeInput = exports.DateTimeInput = (_dec = (0, _testable.testable)(), _
498
515
  timezone: timezone,
499
516
  inputRef: timeInputRef,
500
517
  interaction: interaction,
501
- allowNonStepInput: allowNonStepInput
518
+ allowNonStepInput: allowNonStepInput,
519
+ isRequired: isRequired,
520
+ messages: subComponentMessages
502
521
  }));
503
522
  }
504
523
  }, _DateTimeInput.displayName = "DateTimeInput", _DateTimeInput.DEFAULT_MESSAGE_FORMAT = 'LLLL', _DateTimeInput.propTypes = _props.propTypes, _DateTimeInput.allowedProps = _props.allowedProps, _DateTimeInput.defaultProps = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instructure/ui-date-time-input",
3
- "version": "10.9.1-snapshot-1",
3
+ "version": "10.9.1-snapshot-4",
4
4
  "description": "A UI component that allows to select date and time.",
5
5
  "author": "Instructure, Inc. Engineering and Product Design",
6
6
  "module": "./es/index.js",
@@ -23,8 +23,8 @@
23
23
  },
24
24
  "license": "MIT",
25
25
  "devDependencies": {
26
- "@instructure/ui-babel-preset": "10.9.1-snapshot-1",
27
- "@instructure/ui-test-utils": "10.9.1-snapshot-1",
26
+ "@instructure/ui-babel-preset": "10.9.1-snapshot-4",
27
+ "@instructure/ui-test-utils": "10.9.1-snapshot-4",
28
28
  "@testing-library/jest-dom": "^6.6.3",
29
29
  "@testing-library/react": "^16.0.1",
30
30
  "@testing-library/user-event": "^14.5.2",
@@ -32,20 +32,20 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "@babel/runtime": "^7.26.0",
35
- "@instructure/console": "10.9.1-snapshot-1",
36
- "@instructure/shared-types": "10.9.1-snapshot-1",
37
- "@instructure/ui-a11y-content": "10.9.1-snapshot-1",
38
- "@instructure/ui-buttons": "10.9.1-snapshot-1",
39
- "@instructure/ui-calendar": "10.9.1-snapshot-1",
40
- "@instructure/ui-date-input": "10.9.1-snapshot-1",
41
- "@instructure/ui-form-field": "10.9.1-snapshot-1",
42
- "@instructure/ui-i18n": "10.9.1-snapshot-1",
43
- "@instructure/ui-icons": "10.9.1-snapshot-1",
44
- "@instructure/ui-prop-types": "10.9.1-snapshot-1",
45
- "@instructure/ui-react-utils": "10.9.1-snapshot-1",
46
- "@instructure/ui-testable": "10.9.1-snapshot-1",
47
- "@instructure/ui-time-select": "10.9.1-snapshot-1",
48
- "@instructure/ui-utils": "10.9.1-snapshot-1",
35
+ "@instructure/console": "10.9.1-snapshot-4",
36
+ "@instructure/shared-types": "10.9.1-snapshot-4",
37
+ "@instructure/ui-a11y-content": "10.9.1-snapshot-4",
38
+ "@instructure/ui-buttons": "10.9.1-snapshot-4",
39
+ "@instructure/ui-calendar": "10.9.1-snapshot-4",
40
+ "@instructure/ui-date-input": "10.9.1-snapshot-4",
41
+ "@instructure/ui-form-field": "10.9.1-snapshot-4",
42
+ "@instructure/ui-i18n": "10.9.1-snapshot-4",
43
+ "@instructure/ui-icons": "10.9.1-snapshot-4",
44
+ "@instructure/ui-prop-types": "10.9.1-snapshot-4",
45
+ "@instructure/ui-react-utils": "10.9.1-snapshot-4",
46
+ "@instructure/ui-testable": "10.9.1-snapshot-4",
47
+ "@instructure/ui-time-select": "10.9.1-snapshot-4",
48
+ "@instructure/ui-utils": "10.9.1-snapshot-4",
49
49
  "prop-types": "^15.8.1"
50
50
  },
51
51
  "peerDependencies": {
@@ -173,7 +173,7 @@ describe('<DateTimeInput />', () => {
173
173
  invalidDateTimeMessage={invalidDateTimeMessage}
174
174
  />
175
175
  )
176
- const timeInput = screen.getByLabelText('time-input')
176
+ const timeInput = screen.getByLabelText('time-input *')
177
177
 
178
178
  await userEvent.type(timeInput, '1:00 PM')
179
179
  fireEvent.blur(timeInput)
@@ -165,7 +165,7 @@ class DateTimeInput extends Component<DateTimeInputProps, DateTimeInputState> {
165
165
  ? this.props.invalidDateTimeMessage(parsed.toISOString(true))
166
166
  : this.props.invalidDateTimeMessage
167
167
  }
168
- errorMsg = text ? { text, type: 'error' } : undefined
168
+ errorMsg = text ? { text, type: 'newError' } : undefined
169
169
  return {
170
170
  iso: parsed.clone(),
171
171
  calendarSelectedDate: parsed.clone(),
@@ -277,7 +277,7 @@ class DateTimeInput extends Component<DateTimeInputProps, DateTimeInputState> {
277
277
  event.persist()
278
278
  // timeout is needed here because handleDayClick could be called in the same
279
279
  // frame, and it updates calendarSelectedDate which is read in here.
280
- window.setTimeout(() => {
280
+ setTimeout(() => {
281
281
  if ((event as React.KeyboardEvent).key === 'Enter') {
282
282
  // user pressed enter, use the selected value in the calendar
283
283
  this.updateStateBasedOnDateInput(this.state.calendarSelectedDate, event)
@@ -348,10 +348,17 @@ class DateTimeInput extends Component<DateTimeInputProps, DateTimeInputState> {
348
348
  ? this.props.invalidDateTimeMessage(dateStr ? dateStr : '')
349
349
  : this.props.invalidDateTimeMessage
350
350
  // eslint-disable-next-line no-param-reassign
351
- newState.message = { text: text, type: 'error' }
351
+ newState.message = { text: text, type: 'newError' }
352
352
  }
353
353
  if (this.areDifferentDates(this.state.iso, newState.iso)) {
354
- this.props.onChange?.(e, newState.iso?.toISOString())
354
+ if (typeof this.props.onChange === 'function') {
355
+ const newDate = newState.iso?.toISOString()
356
+ // Timeout is needed here because users might change value in the
357
+ // onChange event lister, which might not execute properly
358
+ setTimeout(() => {
359
+ this.props.onChange?.(e, newDate)
360
+ }, 0)
361
+ }
355
362
  }
356
363
  this.setState(newState)
357
364
  }
@@ -368,7 +375,7 @@ class DateTimeInput extends Component<DateTimeInputProps, DateTimeInputState> {
368
375
  // happens on the target before the relatedTarget gets focus.
369
376
  // The timeout gives it a moment for that to happen
370
377
  if (typeof this.props.onBlur === 'function') {
371
- window.setTimeout(() => {
378
+ setTimeout(() => {
372
379
  this.props.onBlur?.(e)
373
380
  }, 0)
374
381
  }
@@ -439,12 +446,12 @@ class DateTimeInput extends Component<DateTimeInputProps, DateTimeInputState> {
439
446
  // Please note that this causes one hour of time difference in the affected timezones/dates and to
440
447
  // fully solve this bug we need to change to something like luxon which handles this properly
441
448
  if (currDate.clone().format('HH') === '23') {
442
- arr.push(currDate.clone().add({hours: 1}))
449
+ arr.push(currDate.clone().add({ hours: 1 }))
443
450
  } else {
444
451
  arr.push(currDate.clone())
445
452
  }
446
453
 
447
- currDate.add({days: 1})
454
+ currDate.add({ days: 1 })
448
455
  }
449
456
  return arr.map((date) => {
450
457
  const dateStr = date.toISOString()
@@ -560,6 +567,17 @@ class DateTimeInput extends Component<DateTimeInputProps, DateTimeInputState> {
560
567
  allowNonStepInput
561
568
  } = this.props
562
569
 
570
+ const allMessages = [
571
+ ...(showMessages && this.state.message ? [this.state.message] : []),
572
+ ...(messages || [])
573
+ ]
574
+
575
+ const hasError = allMessages.find((m) => m.type === 'newError')
576
+ // if the component is in error state, create an empty error message to pass down to the subcomponents (DateInput and TimeInput) so they get a red outline and red required asterisk
577
+ const subComponentMessages: FormMessage[] = hasError
578
+ ? [{ type: 'newError', text: '' }]
579
+ : []
580
+
563
581
  return (
564
582
  <FormFieldGroup
565
583
  description={description}
@@ -568,6 +586,7 @@ class DateTimeInput extends Component<DateTimeInputProps, DateTimeInputState> {
568
586
  colSpacing={colSpacing}
569
587
  vAlign="top"
570
588
  elementRef={this.handleRef}
589
+ isGroup={false}
571
590
  messages={[
572
591
  ...(showMessages && this.state.message ? [this.state.message] : []),
573
592
  ...(messages || [])
@@ -594,6 +613,7 @@ class DateTimeInput extends Component<DateTimeInputProps, DateTimeInputState> {
594
613
  onRequestRenderNextMonth={this.handleRenderNextMonth}
595
614
  onRequestRenderPrevMonth={this.handleRenderPrevMonth}
596
615
  isRequired={isRequired}
616
+ messages={subComponentMessages}
597
617
  interaction={interaction}
598
618
  renderNavigationLabel={
599
619
  <span>
@@ -617,6 +637,8 @@ class DateTimeInput extends Component<DateTimeInputProps, DateTimeInputState> {
617
637
  inputRef={timeInputRef}
618
638
  interaction={interaction}
619
639
  allowNonStepInput={allowNonStepInput}
640
+ isRequired={isRequired}
641
+ messages={subComponentMessages}
620
642
  />
621
643
  </FormFieldGroup>
622
644
  )