@instructure/ui-date-time-input 8.55.1-snapshot-0 → 8.55.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.
@@ -23,210 +23,766 @@
23
23
  */
24
24
 
25
25
  import React from 'react'
26
- import { render } from '@testing-library/react'
26
+ import { fireEvent, render, screen, waitFor } from '@testing-library/react'
27
+ import userEvent from '@testing-library/user-event'
27
28
  import '@testing-library/jest-dom'
28
- // import { userEvent } from '@testing-library/user-event'
29
- import DateTimeInput from '../index'
29
+
30
+ import { DateTimeInput } from '../index'
31
+ import { ApplyLocale, DateTime } from '@instructure/ui-i18n'
30
32
 
31
33
  describe('<DateTimeInput />', () => {
32
- it('should render', async () => {
34
+ let consoleErrorMock: jest.SpyInstance
35
+
36
+ beforeEach(() => {
37
+ // Mocking console to prevent test output pollution and expect for messages
38
+ consoleErrorMock = jest.spyOn(console, 'error').mockImplementation()
39
+ })
40
+
41
+ afterEach(() => {
42
+ consoleErrorMock.mockRestore()
43
+ })
44
+
45
+ it('should use the default value', () => {
33
46
  const locale = 'en-US'
34
47
  const timezone = 'US/Eastern'
48
+ const dateTime = DateTime.parse('2017-05-01T17:30Z', locale, timezone)
49
+
50
+ render(
51
+ <DateTimeInput
52
+ description="date_time"
53
+ prevMonthLabel="Previous month"
54
+ nextMonthLabel="Next month"
55
+ dateRenderLabel="date-input"
56
+ timeRenderLabel="time-input"
57
+ invalidDateTimeMessage="whoops"
58
+ locale={locale}
59
+ timezone={timezone}
60
+ defaultValue={dateTime.toISOString()}
61
+ />
62
+ )
63
+ const dateInput = screen.getByLabelText('date-input')
64
+ const timeInput = screen.getByLabelText('time-input')
65
+
66
+ expect(dateInput).toHaveValue(dateTime.format('LL'))
67
+ expect(timeInput).toHaveValue(dateTime.format('LT'))
68
+ })
69
+
70
+ it('should use the value', () => {
71
+ const onChange = jest.fn()
72
+ const locale = 'en-US'
73
+ const timezone = 'US/Eastern'
74
+ const dateTime = DateTime.parse('2017-05-01T23:30Z', locale, timezone)
75
+
76
+ render(
77
+ <DateTimeInput
78
+ description="date_time"
79
+ prevMonthLabel="Previous month"
80
+ nextMonthLabel="Next month"
81
+ dateRenderLabel="date-input"
82
+ timeRenderLabel="time-input"
83
+ invalidDateTimeMessage="whoops"
84
+ locale={locale}
85
+ timezone={timezone}
86
+ value={dateTime.toISOString()}
87
+ onChange={onChange}
88
+ />
89
+ )
90
+ const dateInput = screen.getByLabelText('date-input')
91
+ const timeInput = screen.getByLabelText('time-input')
92
+
93
+ expect(dateInput).toHaveValue(dateTime.format('LL'))
94
+ expect(timeInput).toHaveValue(dateTime.format('LT'))
95
+ })
96
+
97
+ it('should prefer value to defaultValue', () => {
98
+ const locale = 'en-US'
99
+ const timezone = 'US/Eastern'
100
+ const value = DateTime.parse('2017-05-01T17:30Z', locale, timezone)
101
+ const defaultValue = DateTime.parse('2016-04-01T17:00Z', locale, timezone)
102
+ const onChange = jest.fn()
103
+
104
+ render(
105
+ <DateTimeInput
106
+ description="date_time"
107
+ prevMonthLabel="Previous month"
108
+ nextMonthLabel="Next month"
109
+ dateRenderLabel="date-input"
110
+ timeRenderLabel="time-input"
111
+ invalidDateTimeMessage="whoops"
112
+ locale={locale}
113
+ timezone={timezone}
114
+ value={value.toISOString()}
115
+ defaultValue={defaultValue.toISOString()}
116
+ onChange={onChange}
117
+ />
118
+ )
119
+ const dateInput = screen.getByLabelText('date-input')
120
+ const timeInput = screen.getByLabelText('time-input')
121
+
122
+ expect(dateInput).toHaveValue(value.format('LL'))
123
+ expect(timeInput).toHaveValue(value.format('LT'))
124
+ })
125
+
126
+ it('should set time to local midnight when only date is set', () => {
127
+ const locale = 'en-US'
128
+ const timezone = 'US/Eastern'
129
+ const dateObj = DateTime.parse('2017-04-01T18:30Z', locale, timezone)
130
+ const date = dateObj.toISOString().split('T')[0]
131
+
132
+ render(
133
+ <DateTimeInput
134
+ description="date_time"
135
+ dateRenderLabel="date-input"
136
+ prevMonthLabel="Previous month"
137
+ nextMonthLabel="Next month"
138
+ timeRenderLabel="time-input"
139
+ invalidDateTimeMessage="whoops"
140
+ locale={locale}
141
+ timezone={timezone}
142
+ defaultValue={date}
143
+ />
144
+ )
145
+ const dateInput = screen.getByLabelText('date-input')
146
+ const timeInput = screen.getByLabelText('time-input')
147
+
148
+ expect(dateInput).toHaveValue(dateObj.format('LL'))
149
+ expect(timeInput).toHaveValue('12:00 AM')
150
+ })
151
+
152
+ it('should call invalidDateTimeMessage if time is set w/o a date and is required', async () => {
153
+ const invalidDateTimeMessage = jest.fn((_rawd) => 'whoops')
35
154
 
36
155
  const { container } = render(
37
156
  <DateTimeInput
38
- description="date time"
157
+ description="date_time"
158
+ dateRenderLabel="date-input"
39
159
  prevMonthLabel="Previous month"
40
160
  nextMonthLabel="Next month"
41
- dateRenderLabel="date"
42
- timeRenderLabel="time"
161
+ timeRenderLabel="time-input"
162
+ locale="en-US"
163
+ timezone="US/Eastern"
164
+ isRequired
165
+ invalidDateTimeMessage={invalidDateTimeMessage}
166
+ />
167
+ )
168
+ const timeInput = screen.getByLabelText('time-input')
169
+
170
+ await userEvent.type(timeInput, '1:00 PM')
171
+ fireEvent.blur(timeInput)
172
+
173
+ await waitFor(() => {
174
+ expect(container).toHaveTextContent('whoops')
175
+ expect(invalidDateTimeMessage).toHaveBeenCalled()
176
+ })
177
+ })
178
+
179
+ it('should not call invalidDateTimeMessage if time is set w/o a date', async () => {
180
+ const invalidDateTimeMessage = jest.fn((_rawd) => 'whoops')
181
+
182
+ render(
183
+ <DateTimeInput
184
+ description="date_time"
185
+ dateRenderLabel="date-input"
186
+ prevMonthLabel="Previous month"
187
+ nextMonthLabel="Next month"
188
+ timeRenderLabel="time-input"
189
+ locale="en-US"
190
+ timezone="US/Eastern"
191
+ invalidDateTimeMessage={invalidDateTimeMessage}
192
+ />
193
+ )
194
+ const timeInput = screen.getByLabelText('time-input')
195
+
196
+ await userEvent.type(timeInput, '1:00 PM{enter}')
197
+
198
+ await waitFor(() => {
199
+ const errorMessages = screen.queryByText('whoops')
200
+
201
+ expect(errorMessages).not.toBeInTheDocument()
202
+ expect(invalidDateTimeMessage).not.toHaveBeenCalled()
203
+ })
204
+ })
205
+
206
+ it('should fire the onChange event when TimeInput value changes', async () => {
207
+ const onChange = jest.fn()
208
+
209
+ const locale = 'en-US'
210
+ const timezone = 'US/Eastern'
211
+ const defaultValue = DateTime.parse('2017-05-01T17:00', locale, timezone)
212
+
213
+ render(
214
+ <DateTimeInput
215
+ description="date_time"
216
+ dateRenderLabel="date-input"
217
+ prevMonthLabel="Previous month"
218
+ nextMonthLabel="Next month"
219
+ timeRenderLabel="time-input"
43
220
  invalidDateTimeMessage="whoops"
44
221
  locale={locale}
45
222
  timezone={timezone}
46
- initialTimeForNewDate="05:05"
223
+ onChange={onChange}
224
+ defaultValue={defaultValue.toISOString()}
225
+ />
226
+ )
227
+ const timeInput = screen.getByLabelText('time-input')
228
+
229
+ fireEvent.change(timeInput, { target: { value: '3:00 AM' } })
230
+ fireEvent.keyDown(timeInput, { key: 'Enter', code: 'Enter' })
231
+ fireEvent.blur(timeInput)
232
+
233
+ await waitFor(() => {
234
+ expect(onChange).toHaveBeenCalled()
235
+ expect(onChange.mock.calls[0][0].target.value).toMatch('3:00 AM')
236
+ })
237
+ })
238
+
239
+ it('should show correct message when TimeInput value changes', () => {
240
+ const { container } = render(
241
+ <DateTimeInput
242
+ description="date_time"
243
+ dateRenderLabel="date-input"
244
+ prevMonthLabel="Previous month"
245
+ nextMonthLabel="Next month"
246
+ timeRenderLabel="time-input"
247
+ invalidDateTimeMessage="whoops"
248
+ locale="en-US"
249
+ timezone="US/Eastern"
250
+ defaultValue="2018-01-18T13:00"
47
251
  />
48
252
  )
49
-
50
- expect(container.firstChild).toBeInTheDocument()
253
+ const timeInput = screen.getByLabelText('time-input')
254
+
255
+ fireEvent.change(timeInput, { target: { value: '5:00 PM' } })
256
+ fireEvent.keyDown(timeInput, { key: 'Enter', code: 'Enter' })
257
+ fireEvent.blur(timeInput)
258
+
259
+ expect(container).toHaveTextContent('Thursday, January 18, 2018 5:00 PM')
51
260
  })
52
261
 
53
- // it("should change value of TimeSelect to initialTimeForNewDate prop's value", async () => {
54
- // const locale = 'en-US'
55
- // const timezone = 'US/Eastern'
262
+ it('should show the formatted date-time message', () => {
263
+ const locale = 'en-US'
264
+ const timezone = 'US/Eastern'
265
+ const defaultValue = DateTime.parse('2017-05-01T17:30Z', locale, timezone)
266
+
267
+ const { container } = render(
268
+ <DateTimeInput
269
+ description="date_time"
270
+ dateRenderLabel="date-input"
271
+ prevMonthLabel="Previous month"
272
+ nextMonthLabel="Next month"
273
+ timeRenderLabel="time-input"
274
+ invalidDateTimeMessage="whoops"
275
+ locale={locale}
276
+ timezone={timezone}
277
+ defaultValue={defaultValue.toISOString()}
278
+ />
279
+ )
56
280
 
57
- // render(
58
- // <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="05:05"
68
- // />
69
- // )
281
+ expect(container).toHaveTextContent('Monday, May 1, 2017 1:30 PM')
282
+ })
283
+
284
+ it('should show the formatted date-time message in the proper locale', () => {
285
+ const timezone = 'US/Eastern'
286
+ const locale = 'fr'
287
+ const defaultValue = DateTime.parse('2017-05-01T17:30Z', locale, timezone)
70
288
 
71
- // const input = screen.getAllByRole('combobox')[0]
289
+ const { container } = render(
290
+ <DateTimeInput
291
+ description="date_time"
292
+ dateRenderLabel="date-input"
293
+ prevMonthLabel="Previous month"
294
+ nextMonthLabel="Next month"
295
+ timeRenderLabel="time-input"
296
+ invalidDateTimeMessage="whoops"
297
+ locale={locale}
298
+ timezone={timezone}
299
+ defaultValue={defaultValue.toISOString()}
300
+ />
301
+ )
302
+ expect(container).toHaveTextContent('1 mai 2017 13:30')
303
+ })
72
304
 
73
- // fireEvent.click(input)
305
+ it('should provide the html elements for date and time input', () => {
306
+ let dref
307
+ let tref
74
308
 
75
- // const firstDay = screen.getByText('15')
309
+ render(
310
+ <DateTimeInput
311
+ description="date_time"
312
+ dateRenderLabel="date-input"
313
+ prevMonthLabel="Previous month"
314
+ nextMonthLabel="Next month"
315
+ timeRenderLabel="time-input"
316
+ invalidDateTimeMessage="whoops"
317
+ locale="en-US"
318
+ timezone="US/Eastern"
319
+ dateInputRef={(el) => {
320
+ dref = el
321
+ }}
322
+ timeInputRef={(el) => {
323
+ tref = el
324
+ }}
325
+ />
326
+ )
327
+ const dateInput = screen.getByLabelText('date-input')
328
+ const timeInput = screen.getByLabelText('time-input')
76
329
 
77
- // await userEvent.click(firstDay)
78
-
79
- // const allInputs = screen.getAllByRole('combobox')
80
- // const targetInput = allInputs.find(
81
- // (input) => (input as HTMLInputElement).value === '5:05 AM'
82
- // )
83
- // expect(targetInput).toBeInTheDocument()
84
- // })
85
-
86
- // it("should throw warning if initialTimeForNewDate prop's value is not HH:MM", async () => {
87
- // const locale = 'en-US'
88
- // const timezone = 'US/Eastern'
89
-
90
- // const consoleError = jest
91
- // .spyOn(console, 'error')
92
- // .mockImplementation(() => {})
93
-
94
- // const initialTimeForNewDate = 'WRONG_FORMAT'
95
-
96
- // render(
97
- // <DateTimeInput
98
- // description="date time"
99
- // prevMonthLabel="Previous month"
100
- // nextMonthLabel="Next month"
101
- // dateRenderLabel="date"
102
- // timeRenderLabel="time"
103
- // invalidDateTimeMessage="whoops"
104
- // locale={locale}
105
- // timezone={timezone}
106
- // initialTimeForNewDate={initialTimeForNewDate}
107
- // />
108
- // )
109
-
110
- // expect(consoleError.mock.calls[0][2]).toContain(
111
- // `Invalid prop \`initialTimeForNewDate\` \`${initialTimeForNewDate}\` supplied to \`DateTimeInput\`, expected a HH:MM formatted string.`
112
- // )
113
-
114
- // const input = screen.getAllByRole('combobox')[0]
115
-
116
- // fireEvent.click(input)
117
-
118
- // const firstDay = screen.getByText('15')
119
-
120
- // await userEvent.click(firstDay)
121
-
122
- // expect(consoleError.mock.calls[1][0]).toBe(
123
- // `Warning: [DateTimeInput] initialTimeForNewDate prop is not in the correct format. Please use HH:MM format.`
124
- // )
125
- // })
126
-
127
- // it('should throw warning if initialTimeForNewDate prop hour and minute values are not in interval', async () => {
128
- // const locale = 'en-US'
129
- // const timezone = 'US/Eastern'
130
-
131
- // const consoleError = jest
132
- // .spyOn(console, 'error')
133
- // .mockImplementation(() => {})
134
-
135
- // const initialTimeForNewDate = '99:99'
136
-
137
- // render(
138
- // <DateTimeInput
139
- // description="date time"
140
- // prevMonthLabel="Previous month"
141
- // nextMonthLabel="Next month"
142
- // dateRenderLabel="date"
143
- // timeRenderLabel="time"
144
- // invalidDateTimeMessage="whoops"
145
- // locale={locale}
146
- // timezone={timezone}
147
- // initialTimeForNewDate={initialTimeForNewDate}
148
- // />
149
- // )
150
-
151
- // const input = screen.getAllByRole('combobox')[0]
152
-
153
- // fireEvent.click(input)
154
-
155
- // const firstDay = screen.getByText('15')
156
-
157
- // await userEvent.click(firstDay)
158
-
159
- // expect(consoleError.mock.calls[0][0]).toContain(
160
- // `Warning: [DateTimeInput] 0 <= hour < 24 and 0 <= minute < 60 for initialTimeForNewDate prop.`
161
- // )
162
- // })
163
-
164
- /*
165
- * TODO write this test with Cypress
166
- * Steps:
167
- * 0. Set initialTimeForNewDate and defaultValue props -> check if defaultValue is rendered in the date input and time input fields
168
- * 1. Clear date input field
169
- * 2. Click on an area outside of the component
170
- * 3. Check if time input field got cleared as well
171
- * 4. Click on the date input and select a date
172
- * 5. Observe if the time input has the value of initialTimeForNewDate
173
- */
174
-
175
- // it('should merge defaultValue and initialTimeForNewDate when the user clears date input', async () => {
176
- // const locale = 'en-US'
177
- // const timezone = 'US/Eastern'
178
- //
179
- // // const consoleWarn = jest.spyOn(console, 'warn').mockImplementation(() => {})
180
- //
181
- // const initialTimeForNewDate = '16:16'
182
- // const defaultValue = '2018-01-18T13:30'
183
- //
184
- // const {container} = render(
185
- // <DateTimeInput
186
- // description="date time"
187
- // prevMonthLabel="Previous month"
188
- // nextMonthLabel="Next month"
189
- // dateRenderLabel="date"
190
- // timeRenderLabel="time"
191
- // invalidDateTimeMessage="whoops"
192
- // defaultValue={defaultValue}
193
- // locale={locale}
194
- // timezone={timezone}
195
- // initialTimeForNewDate={initialTimeForNewDate}
196
- // />
197
- // )
198
- //
199
- // const dateInput = container.querySelector('input')
200
- //
201
- // const timeInput = container.querySelectorAll('input')[1]
202
- //
203
- // fireEvent.change(dateInput as Element, { target: { value: '' } })
204
- //
205
- //
206
- // await waitFor(() => {
207
- // fireEvent.keyDown(dateInput as Element, { key: 'Escape' })
208
- //
209
- // expect(timeInput).toHaveValue('')
210
- // })
211
- //
212
- // // fireEvent.
213
- // // fireEvent.change(timeInput as Element, { target: { value: '' } })
214
- //
215
- //
216
- // const firstDay = screen.getByText('15')
217
- //
218
- // await userEvent.click(firstDay)
219
- //
220
- // const time = screen.getByText('4:16 PM')
221
- //
222
- // expect(time).toBeInTheDocument()
223
- //
224
- // // expect(consoleWarn.mock.calls[0][0]).toContain(
225
- // // `Warning: [DateTimeInput] if defaultValue is set, initialTimeForNewDate will be ignored.`
226
- // // )
227
- // })
228
-
229
- // afterEach(() => {
230
- // jest.resetAllMocks()
231
- // })
330
+ expect(dateInput).toEqual(dref)
331
+ expect(timeInput).toEqual(tref)
332
+ })
333
+
334
+ it('should update message when value prop changes', () => {
335
+ const onChange = jest.fn()
336
+ const locale = 'en-US'
337
+ const timezone = 'US/Eastern'
338
+ const dateTime = DateTime.parse('2017-05-01T17:30Z', locale, timezone)
339
+
340
+ const props = {
341
+ description: 'date_time',
342
+ prevMonthLabel: 'Previous month',
343
+ nextMonthLabel: 'Next month',
344
+ dateRenderLabel: 'date',
345
+ timeRenderLabel: 'time',
346
+ invalidDateTimeMessage: 'whoops',
347
+ locale,
348
+ timezone,
349
+ value: dateTime.toISOString(),
350
+ onChange
351
+ }
352
+
353
+ const { container, rerender } = render(<DateTimeInput {...props} />)
354
+ expect(container).toHaveTextContent('May 1, 2017 1:30 PM')
355
+
356
+ rerender(<DateTimeInput {...props} value="2018-03-29T16:30Z" />)
357
+ expect(container).toHaveTextContent('March 29, 2018 12:30 PM')
358
+ })
359
+
360
+ it('should update message when locale changed', () => {
361
+ const onChange = jest.fn()
362
+ const locale = 'en-US'
363
+ const timezone = 'US/Eastern'
364
+ const dateTime = DateTime.parse('2017-05-01T17:30Z', locale, timezone)
365
+
366
+ const props = {
367
+ description: 'date_time',
368
+ dateRenderLabel: 'date-input',
369
+ prevMonthLabel: 'Previous month',
370
+ nextMonthLabel: 'Next month',
371
+ timeRenderLabel: 'time-input',
372
+ invalidDateTimeMessage: 'whoops',
373
+ locale,
374
+ timezone,
375
+ onChange,
376
+ value: dateTime.toISOString()
377
+ }
378
+
379
+ const { container, rerender } = render(<DateTimeInput {...props} />)
380
+ expect(container).toHaveTextContent('May 1, 2017 1:30 PM')
381
+
382
+ rerender(<DateTimeInput {...props} locale="fr" />)
383
+ expect(container).toHaveTextContent('1 mai 2017 13:30')
384
+ })
385
+
386
+ it('should update message when timezone changed', () => {
387
+ const onChange = jest.fn()
388
+ const locale = 'en-US'
389
+ const timezone = 'US/Eastern'
390
+ const dateTime = DateTime.parse('2017-05-01T17:30Z', locale, timezone)
391
+ const props = {
392
+ description: 'date_time',
393
+ dateRenderLabel: 'date-input',
394
+ prevMonthLabel: 'Previous month',
395
+ nextMonthLabel: 'Next month',
396
+ timeRenderLabel: 'time-input',
397
+ invalidDateTimeMessage: 'whoops',
398
+ locale,
399
+ timezone,
400
+ onChange,
401
+ value: dateTime.toISOString()
402
+ }
403
+
404
+ const { container, rerender } = render(<DateTimeInput {...props} />)
405
+ expect(container).toHaveTextContent('May 1, 2017 1:30 PM')
406
+
407
+ rerender(<DateTimeInput {...props} timezone="Europe/Paris" />)
408
+ expect(container).toHaveTextContent('May 1, 2017 7:30 PM')
409
+ })
410
+
411
+ it('should show error message if initial value is invalid', async () => {
412
+ const { container } = render(
413
+ <DateTimeInput
414
+ description="date_time"
415
+ dateRenderLabel="date-input"
416
+ prevMonthLabel="Previous month"
417
+ nextMonthLabel="Next month"
418
+ timeRenderLabel="time-input"
419
+ invalidDateTimeMessage="whoops"
420
+ value="totally not a date"
421
+ />
422
+ )
423
+ const dateInput = screen.getByLabelText('date-input')
424
+ fireEvent.blur(dateInput)
425
+
426
+ await waitFor(() => {
427
+ expect(container).toHaveTextContent('whoops')
428
+ })
429
+ })
430
+
431
+ it('should show error message if initial defaultValue is invalid', async () => {
432
+ const { container } = render(
433
+ <DateTimeInput
434
+ description="date_time"
435
+ dateRenderLabel="date-input"
436
+ prevMonthLabel="Previous month"
437
+ nextMonthLabel="Next month"
438
+ timeRenderLabel="time-input"
439
+ invalidDateTimeMessage="whoops"
440
+ defaultValue="totally not a date"
441
+ />
442
+ )
443
+ const dateInput = screen.getByLabelText('date-input')
444
+ fireEvent.blur(dateInput)
445
+
446
+ await waitFor(() => {
447
+ expect(container).toHaveTextContent('whoops')
448
+ })
449
+ })
450
+
451
+ it('should update error message when value is invalid', async () => {
452
+ const locale = 'en-US'
453
+ const timezone = 'US/Eastern'
454
+ const dateTime = DateTime.parse('2017-05-01T17:30Z', locale, timezone)
455
+ const props = {
456
+ description: 'date_time',
457
+ dateRenderLabel: 'date-input',
458
+ prevMonthLabel: 'Previous month',
459
+ nextMonthLabel: 'Next month',
460
+ timeRenderLabel: 'time-input',
461
+ invalidDateTimeMessage: 'whoops',
462
+ locale,
463
+ timezone,
464
+ value: dateTime.toISOString()
465
+ }
466
+ const { container, rerender } = render(<DateTimeInput {...props} />)
467
+ expect(container).toHaveTextContent('May 1, 2017 1:30 PM')
468
+
469
+ rerender(<DateTimeInput {...props} value="A very invalid date" />)
470
+ const dateInput = screen.getByLabelText('date-input')
471
+ fireEvent.blur(dateInput)
472
+
473
+ await waitFor(() => {
474
+ expect(container).toHaveTextContent('whoops')
475
+ })
476
+ })
477
+
478
+ it('should show supplied message', async () => {
479
+ const locale = 'en-US'
480
+ const timezone = 'US/Eastern'
481
+ const dateTime = DateTime.parse('2017-05-01T17:30Z', locale, timezone)
482
+ const props = {
483
+ description: 'date_time',
484
+ dateRenderLabel: 'date-input',
485
+ prevMonthLabel: 'Previous month',
486
+ nextMonthLabel: 'Next month',
487
+ timeRenderLabel: 'time-input',
488
+ invalidDateTimeMessage: 'whoops',
489
+ locale,
490
+ timezone,
491
+ value: dateTime.toISOString()
492
+ }
493
+ const { container, rerender } = render(<DateTimeInput {...props} />)
494
+ expect(container).toHaveTextContent('May 1, 2017 1:30 PM')
495
+
496
+ rerender(
497
+ <DateTimeInput
498
+ {...props}
499
+ messages={[{ text: 'message_text', type: 'success' }]}
500
+ />
501
+ )
502
+ const dateInput = screen.getByLabelText('date-input')
503
+ fireEvent.blur(dateInput)
504
+
505
+ await waitFor(() => {
506
+ expect(container).toHaveTextContent('May 1, 2017 1:30 PM')
507
+ expect(container).toHaveTextContent('message_text')
508
+ })
509
+ })
510
+
511
+ it('should not show built in message when `showMessages` is false', async () => {
512
+ const locale = 'en-US'
513
+ const timezone = 'US/Eastern'
514
+ const dateTime = DateTime.parse('2017-05-01T17:30Z', locale, timezone)
515
+ const props = {
516
+ description: 'date_time',
517
+ dateRenderLabel: 'date-input',
518
+ prevMonthLabel: 'Previous month',
519
+ nextMonthLabel: 'Next month',
520
+ timeRenderLabel: 'time-input',
521
+ invalidDateTimeMessage: 'whoops',
522
+ locale,
523
+ timezone,
524
+ value: dateTime.toISOString()
525
+ }
526
+ const { container, rerender } = render(
527
+ <DateTimeInput {...props} showMessages={false} />
528
+ )
529
+ expect(container).not.toHaveTextContent('2017')
530
+
531
+ rerender(
532
+ <DateTimeInput
533
+ {...props}
534
+ showMessages={false}
535
+ messages={[{ text: 'message_text', type: 'success' }]}
536
+ />
537
+ )
538
+ const dateInput = screen.getByLabelText('date-input')
539
+
540
+ fireEvent.blur(dateInput)
541
+
542
+ await waitFor(() => {
543
+ expect(container).not.toHaveTextContent('2017')
544
+ expect(container).toHaveTextContent('message_text')
545
+ })
546
+
547
+ rerender(
548
+ <DateTimeInput
549
+ {...props}
550
+ messages={[{ text: 'message_text', type: 'success' }]}
551
+ showMessages={true}
552
+ />
553
+ )
554
+
555
+ fireEvent.blur(screen.getByLabelText('date-input'))
556
+
557
+ await waitFor(() => {
558
+ expect(container).toHaveTextContent('2017')
559
+ expect(container).toHaveTextContent('message_text')
560
+ })
561
+ })
562
+
563
+ it('should read locale and timezone from context', () => {
564
+ const onChange = jest.fn()
565
+ const dateTime = DateTime.parse('2017-05-01T17:30Z', 'en-US', 'GMT')
566
+ const { container } = render(
567
+ // Africa/Nairobi is GMT +3
568
+ <ApplyLocale locale="fr" timezone="Africa/Nairobi">
569
+ <DateTimeInput
570
+ description="date_time"
571
+ dateRenderLabel="date-input"
572
+ prevMonthLabel="Previous month"
573
+ nextMonthLabel="Next month"
574
+ timeRenderLabel="time-input"
575
+ invalidDateTimeMessage="whoops"
576
+ value={dateTime.toISOString()}
577
+ onChange={onChange}
578
+ />
579
+ </ApplyLocale>
580
+ )
581
+
582
+ expect(container).toHaveTextContent('lundi 1 mai 2017 20:30')
583
+ })
584
+
585
+ it('should format date according to dateFormat', async () => {
586
+ const onChange = jest.fn()
587
+ const dateTime = DateTime.parse('2017-05-01T17:30Z', 'en-US', 'GMT')
588
+ const props = {
589
+ description: 'date_time',
590
+ dateRenderLabel: 'date-input',
591
+ prevMonthLabel: 'Previous month',
592
+ nextMonthLabel: 'Next month',
593
+ timeRenderLabel: 'time-input',
594
+ invalidDateTimeMessage: 'whoops',
595
+ value: dateTime.toISOString(),
596
+ onChange
597
+ }
598
+
599
+ const { rerender } = render(
600
+ <DateTimeInput
601
+ {...props}
602
+ dateFormat="l" // localized numeric date like 9/4/2017
603
+ />
604
+ )
605
+ const dateInput = screen.getByLabelText('date-input')
606
+
607
+ expect(dateInput).toHaveValue('5/1/2017')
608
+
609
+ rerender(<DateTimeInput {...props} dateFormat="yyyy MMMM" />)
610
+
611
+ fireEvent.blur(screen.getByLabelText('date-input'))
612
+
613
+ await waitFor(() => {
614
+ expect(dateInput).toHaveValue('2017 May')
615
+ })
616
+ })
617
+
618
+ it('should format bottom message according to messageFormat', async () => {
619
+ const onChange = jest.fn()
620
+ const dateTime = DateTime.parse('2017-05-01T17:30Z', 'en-US', 'GMT')
621
+ const props = {
622
+ description: 'date_time',
623
+ dateRenderLabel: 'date-input',
624
+ prevMonthLabel: 'Previous month',
625
+ nextMonthLabel: 'Next month',
626
+ timeRenderLabel: 'time-input',
627
+ invalidDateTimeMessage: 'whoops',
628
+ value: dateTime.toISOString(),
629
+ onChange,
630
+ locale: 'en-US',
631
+ timezone: 'US/Eastern'
632
+ }
633
+
634
+ const { container, rerender } = render(<DateTimeInput {...props} />)
635
+ expect(container).toHaveTextContent('Monday, May 1, 2017 1:30 PM')
636
+
637
+ rerender(<DateTimeInput {...props} messageFormat="l, LT" />)
638
+ fireEvent.blur(screen.getByLabelText('date-input'))
639
+
640
+ await waitFor(() => {
641
+ expect(container).toHaveTextContent('5/1/2017, 1:30 PM')
642
+ })
643
+ })
644
+
645
+ it('should update Date and Time inputs when value prop changes', async () => {
646
+ const locale = 'en-US'
647
+ const timezone = 'US/Eastern'
648
+ const dateTime = DateTime.parse('2017-05-01T17:30Z', locale, timezone)
649
+ const props = {
650
+ description: 'date_time',
651
+ dateRenderLabel: 'date-input',
652
+ prevMonthLabel: 'Previous month',
653
+ nextMonthLabel: 'Next month',
654
+ timeRenderLabel: 'time-input',
655
+ invalidDateTimeMessage: 'whoops',
656
+ locale,
657
+ timezone,
658
+ value: dateTime.toISOString()
659
+ }
660
+ const { container, rerender } = render(<DateTimeInput {...props} />)
661
+ expect(container).toHaveTextContent('May 1, 2017 1:30 PM')
662
+
663
+ const newDateStr = '2022-03-29T19:00Z'
664
+ const newDateTime = DateTime.parse(newDateStr, locale, timezone)
665
+
666
+ rerender(<DateTimeInput {...props} value={newDateStr} />)
667
+ const dateInput = screen.getByLabelText('date-input')
668
+ const timeInput = screen.getByLabelText('time-input')
669
+
670
+ fireEvent.blur(dateInput)
671
+
672
+ await waitFor(() => {
673
+ expect(dateInput).toHaveValue(newDateTime.format('LL'))
674
+ expect(timeInput).toHaveValue(newDateTime.format('LT'))
675
+ })
676
+ })
677
+
678
+ it("should throw warning if initialTimeForNewDate prop's value is not HH:MM", async () => {
679
+ const initialTimeForNewDate = 'WRONG_FORMAT'
680
+
681
+ render(
682
+ <DateTimeInput
683
+ description="date_time"
684
+ dateRenderLabel="date-input"
685
+ prevMonthLabel="Previous month"
686
+ nextMonthLabel="Next month"
687
+ timeRenderLabel="time-input"
688
+ invalidDateTimeMessage="whoops"
689
+ locale={'en-US'}
690
+ timezone={'US/Eastern'}
691
+ initialTimeForNewDate={initialTimeForNewDate}
692
+ />
693
+ )
694
+
695
+ expect(consoleErrorMock.mock.calls[0][2]).toEqual(
696
+ expect.stringContaining(
697
+ `Invalid prop \`initialTimeForNewDate\` \`${initialTimeForNewDate}\` supplied to \`DateTimeInput\`, expected a HH:MM formatted string.`
698
+ )
699
+ )
700
+ })
701
+
702
+ it("should throw warning if date select and initialTimeForNewDate prop's value is not HH:MM", async () => {
703
+ const initialTimeForNewDate = 'WRONG_FORMAT'
704
+
705
+ render(
706
+ <DateTimeInput
707
+ description="date_time"
708
+ dateRenderLabel="date-input"
709
+ prevMonthLabel="Previous month"
710
+ nextMonthLabel="Next month"
711
+ timeRenderLabel="time-input"
712
+ invalidDateTimeMessage="whoops"
713
+ locale={'en-US'}
714
+ timezone={'US/Eastern'}
715
+ initialTimeForNewDate={initialTimeForNewDate}
716
+ />
717
+ )
718
+
719
+ const dateInput = screen.getByLabelText('date-input')
720
+
721
+ fireEvent.change(dateInput, { target: { value: 'May 1, 2017' } })
722
+ fireEvent.keyDown(dateInput, { key: 'Enter', code: 'Enter' })
723
+ fireEvent.blur(dateInput)
724
+
725
+ await waitFor(() => {
726
+ expect(consoleErrorMock.mock.calls[0][0]).toBe(
727
+ `Warning: [DateTimeInput] initialTimeForNewDate prop is not in the correct format. Please use HH:MM format.`
728
+ )
729
+ })
730
+ })
731
+
732
+ it('should throw warning if initialTimeForNewDate prop hour and minute values are not in interval', async () => {
733
+ const initialTimeForNewDate = '99:99'
734
+
735
+ render(
736
+ <DateTimeInput
737
+ description="date_time"
738
+ dateRenderLabel="date-input"
739
+ prevMonthLabel="Previous month"
740
+ nextMonthLabel="Next month"
741
+ timeRenderLabel="time-input"
742
+ invalidDateTimeMessage="whoops"
743
+ locale={'en-US'}
744
+ timezone={'US/Eastern'}
745
+ initialTimeForNewDate={initialTimeForNewDate}
746
+ />
747
+ )
748
+ const dateInput = screen.getByLabelText('date-input')
749
+
750
+ fireEvent.change(dateInput, { target: { value: 'May 1, 2017' } })
751
+ fireEvent.keyDown(dateInput, { key: 'Enter', code: 'Enter' })
752
+ fireEvent.blur(dateInput)
753
+
754
+ await waitFor(() => {
755
+ expect(consoleErrorMock.mock.calls[0][0]).toEqual(
756
+ expect.stringContaining(
757
+ `Warning: [DateTimeInput] 0 <= hour < 24 and 0 <= minute < 60 for initialTimeForNewDate prop.`
758
+ )
759
+ )
760
+ })
761
+ })
762
+
763
+ it('should initial render prefer defaultValue time over initialTimeForNewDate', async () => {
764
+ const locale = 'en-US'
765
+ const timezone = 'US/Eastern'
766
+ const initialTimeForNewDate = '16:16'
767
+ const defaultValue = '2018-01-18T13:30'
768
+
769
+ const { container } = render(
770
+ <DateTimeInput
771
+ description="date_time"
772
+ dateRenderLabel="date-input"
773
+ prevMonthLabel="Previous month"
774
+ nextMonthLabel="Next month"
775
+ timeRenderLabel="time-input"
776
+ invalidDateTimeMessage="whoops"
777
+ defaultValue={defaultValue}
778
+ locale={locale}
779
+ timezone={timezone}
780
+ initialTimeForNewDate={initialTimeForNewDate}
781
+ />
782
+ )
783
+ const timeInput = screen.getByLabelText('time-input')
784
+
785
+ expect(timeInput).toHaveValue('1:30 PM')
786
+ expect(container).toHaveTextContent('Thursday, January 18, 2018 1:30 PM')
787
+ })
232
788
  })