@instructure/ui-date-input 9.5.2 → 9.6.1-snapshot-2

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,6 +3,28 @@
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
+ ## [9.6.1-snapshot-2](https://github.com/instructure/instructure-ui/compare/v9.6.0...v9.6.1-snapshot-2) (2024-09-06)
7
+
8
+
9
+ ### Features
10
+
11
+ * **ui-date-input:** improve DateInput2 api, extend docs ([42c83a2](https://github.com/instructure/instructure-ui/commit/42c83a240434dd52233c482292caa556babb5c3a))
12
+
13
+
14
+
15
+
16
+
17
+ # [9.6.0](https://github.com/instructure/instructure-ui/compare/v9.5.2...v9.6.0) (2024-08-14)
18
+
19
+
20
+ ### Features
21
+
22
+ * **ui-calendar,ui-date-input:** improve DateInput2 onChange callback, add date formatting option, extend docs ([4e2c23c](https://github.com/instructure/instructure-ui/commit/4e2c23c3288885e49030f1f471d61b2fed29b54c))
23
+
24
+
25
+
26
+
27
+
6
28
  ## [9.5.2](https://github.com/instructure/instructure-ui/compare/v9.5.1...v9.5.2) (2024-08-05)
7
29
 
8
30
 
@@ -35,16 +35,53 @@ import { passthroughProps } from '@instructure/ui-react-utils';
35
35
  import { ApplyLocaleContext, Locale } from '@instructure/ui-i18n';
36
36
  import { jsx } from '@instructure/emotion';
37
37
  import { propTypes } from './props';
38
- function parseDate(dateString) {
38
+ /*
39
+ * Tries parsing a date in a given timezone, if it's not possible, returns an empty string
40
+ * If parsing is successful an ISO formatted datetime string is returned in UTC timezone
41
+ */
42
+ function timezoneDateToUtc(dateString, timezone) {
43
+ // Don't try to parse short dateString, they are incomplete
44
+ if (dateString.length < 10) {
45
+ return '';
46
+ }
47
+
48
+ // Create a Date object from the input date string
39
49
  const date = new Date(dateString);
40
- // return empty string if not a valid date
41
- return isNaN(date.getTime()) ? '' : date.toISOString();
50
+
51
+ // Check if the date is valid
52
+ if (isNaN(date.getTime())) {
53
+ return '';
54
+ }
55
+
56
+ // snippet from https://stackoverflow.com/a/57842203
57
+ // but it might need to be improved:
58
+ // "This produces incorrect datetimes for several hours surrounding daylight saving times if the
59
+ // computer running the code is in a zone that doesn't obey the same daylight saving shifts as the target zone."
60
+ const utcDate = new Date(date.toLocaleString('en-US', {
61
+ timeZone: 'UTC'
62
+ }));
63
+ const tzDate = new Date(date.toLocaleString('en-US', {
64
+ timeZone: timezone
65
+ }));
66
+ const offset = utcDate.getTime() - tzDate.getTime();
67
+ const newDate = new Date(date.getTime() + offset);
68
+ return newDate.toISOString();
69
+ }
70
+ function defaultDateFormatter(dateString, locale, timezone) {
71
+ return new Date(dateString).toLocaleDateString(locale, {
72
+ month: 'long',
73
+ year: 'numeric',
74
+ day: 'numeric',
75
+ timeZone: timezone
76
+ });
42
77
  }
43
78
 
44
79
  /**
45
80
  ---
46
81
  category: components
47
82
  ---
83
+
84
+ @module experimental
48
85
  **/
49
86
  const DateInput2 = ({
50
87
  renderLabel,
@@ -64,12 +101,33 @@ const DateInput2 = ({
64
101
  locale,
65
102
  timezone,
66
103
  placeholder,
104
+ formatDate = defaultDateFormatter,
105
+ // margin, TODO enable this prop
67
106
  ...rest
68
107
  }) => {
69
- const _useState = useState(''),
108
+ const localeContext = useContext(ApplyLocaleContext);
109
+ const getLocale = () => {
110
+ if (locale) {
111
+ return locale;
112
+ } else if (localeContext.locale) {
113
+ return localeContext.locale;
114
+ }
115
+ // default to the system's locale
116
+ return Locale.browserLocale();
117
+ };
118
+ const getTimezone = () => {
119
+ if (timezone) {
120
+ return timezone;
121
+ } else if (localeContext.timezone) {
122
+ return localeContext.timezone;
123
+ }
124
+ // default to the system's timezone
125
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
126
+ };
127
+ const _useState = useState(value ? formatDate(value, getLocale(), getTimezone()) : ''),
70
128
  _useState2 = _slicedToArray(_useState, 2),
71
- selectedDate = _useState2[0],
72
- setSelectedDate = _useState2[1];
129
+ inputValue = _useState2[0],
130
+ setInputValue = _useState2[1];
73
131
  const _useState3 = useState(messages || []),
74
132
  _useState4 = _slicedToArray(_useState3, 2),
75
133
  inputMessages = _useState4[0],
@@ -78,39 +136,29 @@ const DateInput2 = ({
78
136
  _useState6 = _slicedToArray(_useState5, 2),
79
137
  showPopover = _useState6[0],
80
138
  setShowPopover = _useState6[1];
81
- const localeContext = useContext(ApplyLocaleContext);
82
- useEffect(() => {
83
- // when `value` is changed, validation runs again and removes the error message if validation passes
84
- // but it's NOT adding error message if validation fails for better UX
85
- validateInput(true);
86
- }, [value]);
87
139
  useEffect(() => {
88
140
  setInputMessages(messages || []);
89
141
  }, [messages]);
90
- const handleInputChange = (e, value) => {
91
- onChange === null || onChange === void 0 ? void 0 : onChange(e, value);
92
- // blur event formats the input which should trigger parsing
93
- if (e.type !== 'blur') {
94
- setSelectedDate(parseDate(value));
95
- }
142
+ useEffect(() => {
143
+ validateInput(true);
144
+ }, [inputValue]);
145
+ const handleInputChange = (e, newValue) => {
146
+ setInputValue(newValue);
147
+ const parsedInput = timezoneDateToUtc(newValue, getTimezone());
148
+ onChange === null || onChange === void 0 ? void 0 : onChange(e, parsedInput, newValue);
96
149
  };
97
150
  const handleDateSelected = (dateString, _momentDate, e) => {
98
- const formattedDate = new Date(dateString).toLocaleDateString(getLocale(), {
99
- month: 'long',
100
- year: 'numeric',
101
- day: 'numeric',
102
- timeZone: getTimezone()
103
- });
104
- handleInputChange(e, formattedDate);
105
- setSelectedDate(dateString);
151
+ const formattedDate = formatDate(dateString, getLocale(), getTimezone());
152
+ setInputValue(formattedDate);
106
153
  setShowPopover(false);
154
+ onChange === null || onChange === void 0 ? void 0 : onChange(e, dateString, formattedDate);
107
155
  onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(dateString, true);
108
156
  };
109
157
 
110
158
  // onlyRemoveError is used to remove the error msg immediately when the user inputs a valid date (and don't wait for blur event)
111
159
  const validateInput = (onlyRemoveError = false) => {
112
160
  // don't validate empty input
113
- if (!value || parseDate(value) || selectedDate) {
161
+ if (!inputValue || timezoneDateToUtc(inputValue, getTimezone()) || value) {
114
162
  setInputMessages(messages || []);
115
163
  return true;
116
164
  }
@@ -123,43 +171,25 @@ const DateInput2 = ({
123
171
  }
124
172
  return false;
125
173
  };
126
- const getLocale = () => {
127
- if (locale) {
128
- return locale;
129
- } else if (localeContext.locale) {
130
- return localeContext.locale;
131
- }
132
- return Locale.browserLocale();
133
- };
134
- const getTimezone = () => {
135
- if (timezone) {
136
- return timezone;
137
- } else if (localeContext.timezone) {
138
- return localeContext.timezone;
139
- }
140
- // default to the system's timezone
141
- return Intl.DateTimeFormat().resolvedOptions().timeZone;
142
- };
143
174
  const handleBlur = e => {
144
- const isInputValid = validateInput(false);
145
- if (isInputValid && selectedDate) {
146
- const formattedDate = new Date(selectedDate).toLocaleDateString(getLocale(), {
147
- month: 'long',
148
- year: 'numeric',
149
- day: 'numeric',
150
- timeZone: getTimezone()
151
- });
152
- handleInputChange(e, formattedDate);
175
+ if (value) {
176
+ const formattedDate = formatDate(value, getLocale(), getTimezone());
177
+ if (formattedDate !== inputValue) {
178
+ setInputValue(formattedDate);
179
+ onChange === null || onChange === void 0 ? void 0 : onChange(e, value, formattedDate);
180
+ }
153
181
  }
154
- onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(value, isInputValid);
182
+ validateInput(false);
183
+ onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(value, !!value);
155
184
  onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
156
185
  };
157
186
  return jsx(TextInput, Object.assign({}, passthroughProps(rest), {
187
+ // margin={'large'} TODO add this prop to TextInput
158
188
  renderLabel: renderLabel,
159
189
  onChange: handleInputChange,
160
190
  onBlur: handleBlur,
161
191
  isRequired: isRequired,
162
- value: value,
192
+ value: inputValue,
163
193
  placeholder: placeholder,
164
194
  width: width,
165
195
  size: size,
@@ -185,10 +215,10 @@ const DateInput2 = ({
185
215
  }, jsx(Calendar, {
186
216
  withYearPicker: withYearPicker,
187
217
  onDateSelected: handleDateSelected,
188
- selectedDate: selectedDate,
189
- visibleMonth: selectedDate,
190
- locale: locale,
191
- timezone: timezone,
218
+ selectedDate: value,
219
+ visibleMonth: value,
220
+ locale: getLocale(),
221
+ timezone: getTimezone(),
192
222
  role: "listbox",
193
223
  renderNextMonthButton: jsx(IconButton, {
194
224
  size: "small",
@@ -38,12 +38,11 @@ const propTypes = {
38
38
  isInline: PropTypes.bool,
39
39
  width: PropTypes.string,
40
40
  messages: PropTypes.arrayOf(FormPropTypes.message),
41
- onRequestShowCalendar: PropTypes.func,
42
- onRequestHideCalendar: PropTypes.func,
43
41
  onRequestValidateDate: PropTypes.func,
44
42
  invalidDateErrorMessage: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
45
43
  locale: PropTypes.string,
46
44
  timezone: PropTypes.string,
47
- withYearPicker: PropTypes.object
45
+ withYearPicker: PropTypes.object,
46
+ formatDate: PropTypes.func
48
47
  };
49
48
  export { propTypes };
@@ -44,16 +44,53 @@ var _IconCalendarMonthLin, _IconArrowOpenEndSoli, _IconArrowOpenStartSo;
44
44
  * SOFTWARE.
45
45
  */
46
46
  /** @jsx jsx */
47
- function parseDate(dateString) {
47
+ /*
48
+ * Tries parsing a date in a given timezone, if it's not possible, returns an empty string
49
+ * If parsing is successful an ISO formatted datetime string is returned in UTC timezone
50
+ */
51
+ function timezoneDateToUtc(dateString, timezone) {
52
+ // Don't try to parse short dateString, they are incomplete
53
+ if (dateString.length < 10) {
54
+ return '';
55
+ }
56
+
57
+ // Create a Date object from the input date string
48
58
  const date = new Date(dateString);
49
- // return empty string if not a valid date
50
- return isNaN(date.getTime()) ? '' : date.toISOString();
59
+
60
+ // Check if the date is valid
61
+ if (isNaN(date.getTime())) {
62
+ return '';
63
+ }
64
+
65
+ // snippet from https://stackoverflow.com/a/57842203
66
+ // but it might need to be improved:
67
+ // "This produces incorrect datetimes for several hours surrounding daylight saving times if the
68
+ // computer running the code is in a zone that doesn't obey the same daylight saving shifts as the target zone."
69
+ const utcDate = new Date(date.toLocaleString('en-US', {
70
+ timeZone: 'UTC'
71
+ }));
72
+ const tzDate = new Date(date.toLocaleString('en-US', {
73
+ timeZone: timezone
74
+ }));
75
+ const offset = utcDate.getTime() - tzDate.getTime();
76
+ const newDate = new Date(date.getTime() + offset);
77
+ return newDate.toISOString();
78
+ }
79
+ function defaultDateFormatter(dateString, locale, timezone) {
80
+ return new Date(dateString).toLocaleDateString(locale, {
81
+ month: 'long',
82
+ year: 'numeric',
83
+ day: 'numeric',
84
+ timeZone: timezone
85
+ });
51
86
  }
52
87
 
53
88
  /**
54
89
  ---
55
90
  category: components
56
91
  ---
92
+
93
+ @module experimental
57
94
  **/
58
95
  const DateInput2 = ({
59
96
  renderLabel,
@@ -73,12 +110,33 @@ const DateInput2 = ({
73
110
  locale,
74
111
  timezone,
75
112
  placeholder,
113
+ formatDate = defaultDateFormatter,
114
+ // margin, TODO enable this prop
76
115
  ...rest
77
116
  }) => {
78
- const _useState = (0, _react.useState)(''),
117
+ const localeContext = (0, _react.useContext)(_ApplyLocaleContext.ApplyLocaleContext);
118
+ const getLocale = () => {
119
+ if (locale) {
120
+ return locale;
121
+ } else if (localeContext.locale) {
122
+ return localeContext.locale;
123
+ }
124
+ // default to the system's locale
125
+ return _Locale.Locale.browserLocale();
126
+ };
127
+ const getTimezone = () => {
128
+ if (timezone) {
129
+ return timezone;
130
+ } else if (localeContext.timezone) {
131
+ return localeContext.timezone;
132
+ }
133
+ // default to the system's timezone
134
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
135
+ };
136
+ const _useState = (0, _react.useState)(value ? formatDate(value, getLocale(), getTimezone()) : ''),
79
137
  _useState2 = (0, _slicedToArray2.default)(_useState, 2),
80
- selectedDate = _useState2[0],
81
- setSelectedDate = _useState2[1];
138
+ inputValue = _useState2[0],
139
+ setInputValue = _useState2[1];
82
140
  const _useState3 = (0, _react.useState)(messages || []),
83
141
  _useState4 = (0, _slicedToArray2.default)(_useState3, 2),
84
142
  inputMessages = _useState4[0],
@@ -87,39 +145,29 @@ const DateInput2 = ({
87
145
  _useState6 = (0, _slicedToArray2.default)(_useState5, 2),
88
146
  showPopover = _useState6[0],
89
147
  setShowPopover = _useState6[1];
90
- const localeContext = (0, _react.useContext)(_ApplyLocaleContext.ApplyLocaleContext);
91
- (0, _react.useEffect)(() => {
92
- // when `value` is changed, validation runs again and removes the error message if validation passes
93
- // but it's NOT adding error message if validation fails for better UX
94
- validateInput(true);
95
- }, [value]);
96
148
  (0, _react.useEffect)(() => {
97
149
  setInputMessages(messages || []);
98
150
  }, [messages]);
99
- const handleInputChange = (e, value) => {
100
- onChange === null || onChange === void 0 ? void 0 : onChange(e, value);
101
- // blur event formats the input which should trigger parsing
102
- if (e.type !== 'blur') {
103
- setSelectedDate(parseDate(value));
104
- }
151
+ (0, _react.useEffect)(() => {
152
+ validateInput(true);
153
+ }, [inputValue]);
154
+ const handleInputChange = (e, newValue) => {
155
+ setInputValue(newValue);
156
+ const parsedInput = timezoneDateToUtc(newValue, getTimezone());
157
+ onChange === null || onChange === void 0 ? void 0 : onChange(e, parsedInput, newValue);
105
158
  };
106
159
  const handleDateSelected = (dateString, _momentDate, e) => {
107
- const formattedDate = new Date(dateString).toLocaleDateString(getLocale(), {
108
- month: 'long',
109
- year: 'numeric',
110
- day: 'numeric',
111
- timeZone: getTimezone()
112
- });
113
- handleInputChange(e, formattedDate);
114
- setSelectedDate(dateString);
160
+ const formattedDate = formatDate(dateString, getLocale(), getTimezone());
161
+ setInputValue(formattedDate);
115
162
  setShowPopover(false);
163
+ onChange === null || onChange === void 0 ? void 0 : onChange(e, dateString, formattedDate);
116
164
  onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(dateString, true);
117
165
  };
118
166
 
119
167
  // onlyRemoveError is used to remove the error msg immediately when the user inputs a valid date (and don't wait for blur event)
120
168
  const validateInput = (onlyRemoveError = false) => {
121
169
  // don't validate empty input
122
- if (!value || parseDate(value) || selectedDate) {
170
+ if (!inputValue || timezoneDateToUtc(inputValue, getTimezone()) || value) {
123
171
  setInputMessages(messages || []);
124
172
  return true;
125
173
  }
@@ -132,43 +180,25 @@ const DateInput2 = ({
132
180
  }
133
181
  return false;
134
182
  };
135
- const getLocale = () => {
136
- if (locale) {
137
- return locale;
138
- } else if (localeContext.locale) {
139
- return localeContext.locale;
140
- }
141
- return _Locale.Locale.browserLocale();
142
- };
143
- const getTimezone = () => {
144
- if (timezone) {
145
- return timezone;
146
- } else if (localeContext.timezone) {
147
- return localeContext.timezone;
148
- }
149
- // default to the system's timezone
150
- return Intl.DateTimeFormat().resolvedOptions().timeZone;
151
- };
152
183
  const handleBlur = e => {
153
- const isInputValid = validateInput(false);
154
- if (isInputValid && selectedDate) {
155
- const formattedDate = new Date(selectedDate).toLocaleDateString(getLocale(), {
156
- month: 'long',
157
- year: 'numeric',
158
- day: 'numeric',
159
- timeZone: getTimezone()
160
- });
161
- handleInputChange(e, formattedDate);
184
+ if (value) {
185
+ const formattedDate = formatDate(value, getLocale(), getTimezone());
186
+ if (formattedDate !== inputValue) {
187
+ setInputValue(formattedDate);
188
+ onChange === null || onChange === void 0 ? void 0 : onChange(e, value, formattedDate);
189
+ }
162
190
  }
163
- onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(value, isInputValid);
191
+ validateInput(false);
192
+ onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(value, !!value);
164
193
  onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
165
194
  };
166
195
  return (0, _emotion.jsx)(_TextInput.TextInput, Object.assign({}, (0, _passthroughProps.passthroughProps)(rest), {
196
+ // margin={'large'} TODO add this prop to TextInput
167
197
  renderLabel: renderLabel,
168
198
  onChange: handleInputChange,
169
199
  onBlur: handleBlur,
170
200
  isRequired: isRequired,
171
- value: value,
201
+ value: inputValue,
172
202
  placeholder: placeholder,
173
203
  width: width,
174
204
  size: size,
@@ -194,10 +224,10 @@ const DateInput2 = ({
194
224
  }, (0, _emotion.jsx)(_Calendar.Calendar, {
195
225
  withYearPicker: withYearPicker,
196
226
  onDateSelected: handleDateSelected,
197
- selectedDate: selectedDate,
198
- visibleMonth: selectedDate,
199
- locale: locale,
200
- timezone: timezone,
227
+ selectedDate: value,
228
+ visibleMonth: value,
229
+ locale: getLocale(),
230
+ timezone: getTimezone(),
201
231
  role: "listbox",
202
232
  renderNextMonthButton: (0, _emotion.jsx)(_IconButton.IconButton, {
203
233
  size: "small",
@@ -45,11 +45,10 @@ const propTypes = exports.propTypes = {
45
45
  isInline: _propTypes.default.bool,
46
46
  width: _propTypes.default.string,
47
47
  messages: _propTypes.default.arrayOf(_FormPropTypes.FormPropTypes.message),
48
- onRequestShowCalendar: _propTypes.default.func,
49
- onRequestHideCalendar: _propTypes.default.func,
50
48
  onRequestValidateDate: _propTypes.default.func,
51
49
  invalidDateErrorMessage: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.string]),
52
50
  locale: _propTypes.default.string,
53
51
  timezone: _propTypes.default.string,
54
- withYearPicker: _propTypes.default.object
52
+ withYearPicker: _propTypes.default.object,
53
+ formatDate: _propTypes.default.func
55
54
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instructure/ui-date-input",
3
- "version": "9.5.2",
3
+ "version": "9.6.1-snapshot-2",
4
4
  "description": "A UI component library made by Instructure Inc.",
5
5
  "author": "Instructure, Inc. Engineering and Product Design",
6
6
  "module": "./es/index.js",
@@ -23,11 +23,11 @@
23
23
  },
24
24
  "license": "MIT",
25
25
  "devDependencies": {
26
- "@instructure/ui-axe-check": "9.5.2",
27
- "@instructure/ui-babel-preset": "9.5.2",
28
- "@instructure/ui-buttons": "9.5.2",
29
- "@instructure/ui-scripts": "9.5.2",
30
- "@instructure/ui-test-utils": "9.5.2",
26
+ "@instructure/ui-axe-check": "9.6.1-snapshot-2",
27
+ "@instructure/ui-babel-preset": "9.6.1-snapshot-2",
28
+ "@instructure/ui-buttons": "9.6.1-snapshot-2",
29
+ "@instructure/ui-scripts": "9.6.1-snapshot-2",
30
+ "@instructure/ui-test-utils": "9.6.1-snapshot-2",
31
31
  "@testing-library/jest-dom": "^6.4.6",
32
32
  "@testing-library/react": "^15.0.7",
33
33
  "@testing-library/user-event": "^14.5.2",
@@ -35,20 +35,20 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@babel/runtime": "^7.24.5",
38
- "@instructure/emotion": "9.5.2",
39
- "@instructure/shared-types": "9.5.2",
40
- "@instructure/ui-calendar": "9.5.2",
41
- "@instructure/ui-form-field": "9.5.2",
42
- "@instructure/ui-i18n": "9.5.2",
43
- "@instructure/ui-icons": "9.5.2",
44
- "@instructure/ui-popover": "9.5.2",
45
- "@instructure/ui-position": "9.5.2",
46
- "@instructure/ui-prop-types": "9.5.2",
47
- "@instructure/ui-react-utils": "9.5.2",
48
- "@instructure/ui-selectable": "9.5.2",
49
- "@instructure/ui-testable": "9.5.2",
50
- "@instructure/ui-text-input": "9.5.2",
51
- "@instructure/ui-utils": "9.5.2",
38
+ "@instructure/emotion": "9.6.1-snapshot-2",
39
+ "@instructure/shared-types": "9.6.1-snapshot-2",
40
+ "@instructure/ui-calendar": "9.6.1-snapshot-2",
41
+ "@instructure/ui-form-field": "9.6.1-snapshot-2",
42
+ "@instructure/ui-i18n": "9.6.1-snapshot-2",
43
+ "@instructure/ui-icons": "9.6.1-snapshot-2",
44
+ "@instructure/ui-popover": "9.6.1-snapshot-2",
45
+ "@instructure/ui-position": "9.6.1-snapshot-2",
46
+ "@instructure/ui-prop-types": "9.6.1-snapshot-2",
47
+ "@instructure/ui-react-utils": "9.6.1-snapshot-2",
48
+ "@instructure/ui-selectable": "9.6.1-snapshot-2",
49
+ "@instructure/ui-testable": "9.6.1-snapshot-2",
50
+ "@instructure/ui-text-input": "9.6.1-snapshot-2",
51
+ "@instructure/ui-utils": "9.6.1-snapshot-2",
52
52
  "moment-timezone": "^0.5.45",
53
53
  "prop-types": "^15.8.1"
54
54
  },