Skip to content

Commit 67a968a

Browse files
committed
feat(ui-select,ui-date-time-input): rework DateTimeInput and replace DateInput v1 with DateInput v2 in DateTimeInput
BREAKING CHANGE: prevMonthLabel prop removed (use screenReaderLabels.prevMonthButton instead) nextMonthLabel prop removed (use screenReaderLabels.nextMonthButton instead) renderWeekdayLabels prop removed dateFormat type changed: string → string { parser: (input: string) => Date null, formatter: (date: Date) => string } screenReaderLabels is a new required prop dateFormat default changed: Moment's 'LL' (long month name) → locale's default date format INSTUI-4791
1 parent 8c5a755 commit 67a968a

11 files changed

Lines changed: 1637 additions & 149 deletions

File tree

cypress/component/DateTimeInput.cy.tsx

Lines changed: 65 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,11 @@ describe('<DateInput/>', () => {
3939
cy.mount(
4040
<DateTimeInput
4141
description="date time description"
42-
prevMonthLabel="Previous month"
43-
nextMonthLabel="Next month"
42+
screenReaderLabels={{
43+
calendarIcon: 'Choose date',
44+
prevMonthButton: 'Previous month',
45+
nextMonthButton: 'Next month'
46+
}}
4447
dateRenderLabel="date-input label"
4548
timeRenderLabel="time-input label"
4649
invalidDateTimeMessage="whoops"
@@ -56,43 +59,40 @@ describe('<DateInput/>', () => {
5659
cy.contains('time-input label')
5760
cy.contains('Thursday, January 18, 2018 1:30 PM')
5861

59-
cy.get('input[id^="Selectable_"]').as('dateInput')
62+
cy.get('input[id^="TextInput_"]').as('dateInput')
6063
cy.get('input[id^="Select_"]').as('timeInput')
6164

62-
cy.get('@dateInput').should('have.value', 'January 18, 2018')
65+
cy.get('@dateInput').should('have.value', '1/18/2018')
6366
cy.get('@timeInput').should('have.value', '1:30 PM')
6467

6568
cy.get('@dateInput').clear().blur()
6669

6770
cy.get('@timeInput').should('have.value', '')
6871
cy.wrap(onChange).should('have.been.called')
6972

70-
cy.get('@dateInput').realClick().wait(100)
73+
cy.contains('button', 'Choose date').realClick().wait(100)
74+
75+
cy.contains('button', '22').realClick().wait(100)
7176

72-
cy.contains('button', '22')
73-
.realClick()
74-
.wait(100)
75-
.then(($btn) => {
76-
const selectedDateId = $btn.attr('id')!
77-
const selectedDateValue = DateTime.parse(
78-
selectedDateId,
77+
cy.wrap(onChange)
78+
.should('have.been.called')
79+
.then((spy) => {
80+
const selectedDateId: string = spy.lastCall.args[1]
81+
const selectedDateValue = new Date(selectedDateId).toLocaleDateString(
7982
'en-US',
80-
'US/Eastern'
81-
).format('LL')
83+
{
84+
timeZone: 'US/Eastern',
85+
calendar: 'gregory',
86+
numberingSystem: 'latn'
87+
}
88+
)
8289

8390
cy.get('@dateInput').should('have.value', selectedDateValue)
8491
cy.get('@timeInput').should('have.value', '4:16 PM')
8592

86-
cy.wrap(onChange)
87-
.should('have.been.called')
88-
.then((spy) => {
89-
const lastCallFirstArg = spy.lastCall.args[1]
90-
91-
const lastCallDatePart = lastCallFirstArg.split('T')[0]
92-
const expectedDatePart = selectedDateId.split('T')[0]
93+
const lastCallDatePart = selectedDateId.split('T')[0]
9394

94-
expect(lastCallDatePart).to.equal(expectedDatePart)
95-
})
95+
expect(lastCallDatePart).to.include('-22')
9696
})
9797
})
9898

@@ -103,16 +103,19 @@ describe('<DateInput/>', () => {
103103
<DateTimeInput
104104
description="date_time"
105105
dateRenderLabel="date-input"
106-
prevMonthLabel="Previous month"
107-
nextMonthLabel="Next month"
106+
screenReaderLabels={{
107+
calendarIcon: 'Open calendar',
108+
prevMonthButton: 'Previous month',
109+
nextMonthButton: 'Next month'
110+
}}
108111
timeRenderLabel="time-input"
109112
invalidDateTimeMessage="whoops"
110113
locale="en-US"
111114
timezone="US/Eastern"
112115
onChange={onChange}
113116
/>
114117
)
115-
cy.get('input[id^="Selectable_"]').as('dateInput')
118+
cy.get('input[id^="TextInput_"]').as('dateInput')
116119
cy.get('@dateInput').realClick().wait(100)
117120
cy.get('@dateInput').type('Not a date{enter}')
118121
cy.get('@dateInput').blur()
@@ -128,8 +131,11 @@ describe('<DateInput/>', () => {
128131
cy.mount(
129132
<DateTimeInput
130133
description="date_time"
131-
prevMonthLabel="Previous month"
132-
nextMonthLabel="Next month"
134+
screenReaderLabels={{
135+
calendarIcon: 'Open calendar',
136+
prevMonthButton: 'Previous month',
137+
nextMonthButton: 'Next month'
138+
}}
133139
dateRenderLabel="date-input"
134140
timeRenderLabel="time-input"
135141
invalidDateTimeMessage="whoops"
@@ -140,9 +146,9 @@ describe('<DateInput/>', () => {
140146
disabledDateTimeMessage={errorMsg}
141147
/>
142148
)
143-
cy.get('input[id^="Selectable_"]').as('dateInput')
149+
cy.get('input[id^="TextInput_"]').as('dateInput')
144150
cy.get('body').should('contain', errorMsg)
145-
cy.get('@dateInput').clear().type(`05/18/2017{enter}`)
151+
cy.get('@dateInput').clear().type(`05/18/2017`).blur()
146152

147153
cy.get('body').should('not.contain', errorMsg)
148154
})
@@ -160,8 +166,11 @@ describe('<DateInput/>', () => {
160166
cy.mount(
161167
<DateTimeInput
162168
description="date_time"
163-
prevMonthLabel="Previous month"
164-
nextMonthLabel="Next month"
169+
screenReaderLabels={{
170+
calendarIcon: 'Open calendar',
171+
prevMonthButton: 'Previous month',
172+
nextMonthButton: 'Next month'
173+
}}
165174
dateRenderLabel="date-input"
166175
timeRenderLabel="time-input"
167176
invalidDateTimeMessage="whoops"
@@ -172,9 +181,9 @@ describe('<DateInput/>', () => {
172181
disabledDateTimeMessage={errorMsg}
173182
/>
174183
)
175-
cy.get('input[id^="Selectable_"]').as('dateInput')
184+
cy.get('input[id^="TextInput_"]').as('dateInput')
176185
cy.get('body').should('contain', errorMsgText)
177-
cy.get('@dateInput').clear().type(`May 18, 2022{enter}`)
186+
cy.get('@dateInput').clear().type(`May 18, 2022`).blur()
178187

179188
cy.get('body').should('not.contain', errorMsgText)
180189
})
@@ -187,8 +196,11 @@ describe('<DateInput/>', () => {
187196
const props = {
188197
description: 'date_time',
189198
dateRenderLabel: 'date-input',
190-
prevMonthLabel: 'Previous month',
191-
nextMonthLabel: 'Next month',
199+
screenReaderLabels: {
200+
calendarIcon: 'Open calendar',
201+
prevMonthButton: 'Previous month',
202+
nextMonthButton: 'Next month'
203+
},
192204
timeRenderLabel: 'time-input',
193205
invalidDateTimeMessage: 'whoops',
194206
locale,
@@ -197,10 +209,10 @@ describe('<DateInput/>', () => {
197209

198210
cy.mount(<DateTimeInput {...props} value={dateTime.toISOString()} />)
199211

200-
cy.get('input[id^="Selectable_"]').as('dateInput')
212+
cy.get('input[id^="TextInput_"]').as('dateInput')
201213
cy.get('input[id^="Select_"]').as('timeInput')
202214

203-
cy.get('@dateInput').should('have.value', 'May 1, 2017')
215+
cy.get('@dateInput').should('have.value', '5/1/2017')
204216
cy.get('@timeInput').should('have.value', '1:30 PM')
205217
cy.get('body').should('contain', 'May 1, 2017 1:30 PM')
206218

@@ -215,11 +227,11 @@ describe('<DateInput/>', () => {
215227
const newDateStr = '2022-03-29T19:00Z'
216228
cy.mount(<DateTimeInput {...props} value={newDateStr} />)
217229

218-
cy.get('@dateInput').should('have.value', 'March 29, 2022')
230+
cy.get('@dateInput').should('have.value', '3/29/2022')
219231
cy.get('@timeInput').should('have.value', '3:00 PM')
220232
cy.get('body').should('contain', 'March 29, 2022 3:00 PM')
221233

222-
cy.get('@dateInput').clear().type('{esc}')
234+
cy.get('@dateInput').clear().blur()
223235

224236
cy.get('@dateInput').should('have.value', '')
225237
cy.get('@timeInput').should('have.value', '')
@@ -234,8 +246,11 @@ describe('<DateInput/>', () => {
234246
<DateTimeInput
235247
description="date_time"
236248
dateRenderLabel="date-input"
237-
prevMonthLabel="Previous month"
238-
nextMonthLabel="Next month"
249+
screenReaderLabels={{
250+
calendarIcon: 'Open calendar',
251+
prevMonthButton: 'Previous month',
252+
nextMonthButton: 'Next month'
253+
}}
239254
timeRenderLabel="time-input"
240255
invalidDateTimeMessage="whoops"
241256
locale="en-US"
@@ -244,12 +259,12 @@ describe('<DateInput/>', () => {
244259
allowNonStepInput={true}
245260
/>
246261
)
247-
cy.get('input[id^="Selectable_"]').as('dateInput')
262+
cy.get('input[id^="TextInput_"]').as('dateInput')
248263
cy.get('input[id^="Select_"]').as('timeInput')
249264

250265
cy.get('@timeInput').clear().type(`7:34 PM`)
251266

252-
cy.get('@dateInput').clear().type(`May 1, 2017{enter}`)
267+
cy.get('@dateInput').clear().type(`May 1, 2017`).blur()
253268

254269
cy.wrap(onChange)
255270
.should('have.been.called')
@@ -268,16 +283,19 @@ describe('<DateInput/>', () => {
268283
<DateTimeInput
269284
description="date_time"
270285
dateRenderLabel="date-input"
271-
prevMonthLabel="Previous month"
272-
nextMonthLabel="Next month"
286+
screenReaderLabels={{
287+
calendarIcon: 'Open calendar',
288+
prevMonthButton: 'Previous month',
289+
nextMonthButton: 'Next month'
290+
}}
273291
timeRenderLabel="time-input"
274292
invalidDateTimeMessage="whoops"
275293
locale={locale}
276294
timezone={timezone}
277295
initialTimeForNewDate="05:05"
278296
/>
279297
)
280-
cy.get('input[id^="Selectable_"]').as('dateInput')
298+
cy.get('input[id^="TextInput_"]').as('dateInput')
281299
cy.get('input[id^="Select_"]').as('timeInput')
282300

283301
cy.get('@dateInput').clear().type(`May 1, 2017{enter}`)

docs/guides/upgrade-guide.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,54 @@ Now that InstUI supports component versioning, we no longer need the separate `D
402402
- **[DateInput v1](/v11_6/DateInput)** (up to v11.6) — the original component. **Deprecated.** Does not support the new theming system.
403403
- **[DateInput2 v1](/v11_6/DateInput2)****Deprecated.** Will not get a v2 and does not support the new theming system. If you're using `DateInput2`, switch your import to `DateInput` (from v11.7) — the API is identical, no other code changes needed.
404404

405+
### DateTimeInput
406+
407+
<!-- TODO: add a codemod for the removed/renamed props (prevMonthLabel, nextMonthLabel → screenReaderLabels) -->
408+
409+
**Removed props:**
410+
411+
| Removed prop | Replacement |
412+
| --------------------- | ------------------------------------ |
413+
| `renderWeekdayLabels` | Built in — no replacement needed |
414+
| `prevMonthLabel` | `screenReaderLabels.prevMonthButton` |
415+
| `nextMonthLabel` | `screenReaderLabels.nextMonthButton` |
416+
417+
**Changed props:**
418+
419+
| Prop | old API | new API |
420+
| ------------ | ------------------------------------- | --------------------------------------------------------- |
421+
| `dateFormat` | Moment.js format string (e.g. `'LL'`) | Locale string (e.g. `'en-US'`) or `{ parser, formatter }` |
422+
423+
If you were passing a Moment format string like `dateFormat="LL"`, replace it with a locale string or a custom `{ parser, formatter }` object. If you were relying on the default `'LL'` format, note that v2 now uses the locale's default date format (e.g. `1/18/2018` in `en-US`) instead of the long format (e.g. `January 18, 2018`). To preserve the long format, pass a custom `{ parser, formatter }` object.
424+
425+
**New props:**
426+
427+
| New prop | Description |
428+
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
429+
| `screenReaderLabels` | **Required.** Object containing accessible labels: `calendarIcon`, `prevMonthButton`, `nextMonthButton`, `datePickerDialog?`. |
430+
| `withYearPicker` | Optional. Enables a year dropdown in the calendar. |
431+
432+
```js
433+
---
434+
type: code
435+
---
436+
// old API
437+
<DateTimeInput
438+
prevMonthLabel="Previous month"
439+
nextMonthLabel="Next month"
440+
/>
441+
442+
// new API
443+
<DateTimeInput
444+
screenReaderLabels={{
445+
calendarIcon: 'Open calendar',
446+
prevMonthButton: 'Previous month',
447+
nextMonthButton: 'Next month',
448+
datePickerDialog: 'Date picker' // optional
449+
}}
450+
/>
451+
```
452+
405453
### DataPermissionLevels
406454

407455
```js

packages/ui-date-time-input/package.json

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -72,18 +72,18 @@
7272
"default": "./es/exports/a.js"
7373
},
7474
"./v11_7": {
75-
"src": "./src/exports/a.ts",
76-
"types": "./types/exports/a.d.ts",
77-
"import": "./es/exports/a.js",
78-
"require": "./lib/exports/a.js",
79-
"default": "./es/exports/a.js"
75+
"src": "./src/exports/b.ts",
76+
"types": "./types/exports/b.d.ts",
77+
"import": "./es/exports/b.js",
78+
"require": "./lib/exports/b.js",
79+
"default": "./es/exports/b.js"
8080
},
8181
"./latest": {
82-
"src": "./src/exports/a.ts",
83-
"types": "./types/exports/a.d.ts",
84-
"import": "./es/exports/a.js",
85-
"require": "./lib/exports/a.js",
86-
"default": "./es/exports/a.js"
82+
"src": "./src/exports/b.ts",
83+
"types": "./types/exports/b.d.ts",
84+
"import": "./es/exports/b.js",
85+
"require": "./lib/exports/b.js",
86+
"default": "./es/exports/b.js"
8787
}
8888
}
8989
}

0 commit comments

Comments
 (0)