Skip to content

Commit 1314935

Browse files
authored
fix: date format (#6162)
1 parent b5d5e06 commit 1314935

File tree

11 files changed

+102
-32
lines changed

11 files changed

+102
-32
lines changed

.changeset/fruity-crabs-smoke.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@ultraviolet/ui": minor
3+
---
4+
5+
`DateInput`: change date format from MMDDYYYY to DDMMYYYY and fix date input when min/max date

packages/ui/src/components/DateInput/__stories__/Clearable.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export const Clearable = Template.bind({})
55
Clearable.args = {
66
clearable: true,
77
label: 'Clearable',
8-
placeholder: 'MM-DD-YYYY',
8+
placeholder: 'DD-MM-YYYY',
99
}
1010

1111
Clearable.decorators = [

packages/ui/src/components/DateInput/__stories__/MinMax.stories.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ MinMax.parameters = {
1212

1313
MinMax.args = {
1414
label: 'Date',
15-
maxDate: new Date('December 25, 1995 03:24:00'),
16-
minDate: new Date('December 12, 1995 03:24:00'),
15+
maxDate: new Date('December 25, 1995 00:00:00'),
16+
minDate: new Date('December 12, 1995 00:00:00'),
1717
value: new Date('1995-12-17T03:24:00'),
18+
onChange: console.log,
1819
}
1920

2021
MinMax.decorators = [

packages/ui/src/components/DateInput/__stories__/Playground.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Template } from './Template'
33
export const Playground = Template.bind({})
44

55
Playground.args = {
6-
placeholder: 'MM-DD-YYYY',
6+
placeholder: 'DD-MM-YYYY',
77
}
88

99
Playground.decorators = [

packages/ui/src/components/DateInput/__stories__/Template.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ export const Template: StoryFn<typeof DateInput> = props => (
88

99
Template.args = {
1010
label: 'Date Input',
11-
placeholder: 'MM-DD-YYYY',
11+
placeholder: 'DD-MM-YYYY',
1212
required: true,
1313
}

packages/ui/src/components/DateInput/__tests__/__snapshots__/index.test.tsx.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1226,7 +1226,7 @@ exports[`dateInput > renders correctly with a array of dates to exclude 1`] = `
12261226
id="_r_af_"
12271227
placeholder="YYYY-MM-DD"
12281228
type="text"
1229-
value="12/11/1995"
1229+
value="11/12/1995"
12301230
/>
12311231
<div
12321232
class="styles__q3q8043 styles__toi52u0 styles_alignItems_center_xxsmall__toi52ud styles_flexDirection_row_xxsmall__toi52u2j"

packages/ui/src/components/DateInput/__tests__/helper.test.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { describe, expect, test } from 'vitest'
22

33
import {
44
addZero,
5+
createDate,
56
formatValue,
67
getMonthFirstDay,
78
getNextMonth,
@@ -65,14 +66,14 @@ describe('helper functions dateInput', () => {
6566
})
6667

6768
test('formatValue should work with default formatting', () => {
68-
expect(formatValue(date, null, false, false)).toBe('11/20/2000')
69+
expect(formatValue(date, null, false, false)).toBe('20/11/2000')
6970
expect(formatValue(date, null, true, false)).toBe('11/2000')
70-
expect(formatValue(date, null, false, true)).toBe('11/20/2000')
71+
expect(formatValue(date, null, false, true)).toBe('20/11/2000')
7172

7273
expect(formatValue(null, rangeDate, false, false)).toBe(undefined)
7374
expect(formatValue(null, rangeDate, true, true)).toBe('10/2000 - 10/2000')
7475
expect(formatValue(null, rangeDate, false, true)).toBe(
75-
'10/20/2000 - 10/31/2000',
76+
'20/10/2000 - 31/10/2000',
7677
)
7778
})
7879

@@ -87,4 +88,25 @@ describe('helper functions dateInput', () => {
8788
'2000 - 2000',
8889
)
8990
})
91+
92+
test('createDate should work', () => {
93+
expect(createDate('12/02/2020', false)).toEqual(new Date(2020, 1, 12))
94+
expect(createDate('12-02-2020', false)).toEqual(new Date(2020, 1, 12))
95+
expect(createDate('12 may 2020', false)).toEqual(new Date(2020, 4, 12))
96+
})
97+
98+
test('createDate should work with min and maxDate', () => {
99+
expect(createDate('12/02/2020', false, new Date(2020, 1, 15))).toEqual(
100+
new Date(2020, 1, 15),
101+
)
102+
expect(
103+
createDate('12/02/2020', false, undefined, new Date(2020, 1, 10)),
104+
).toEqual(new Date(2020, 1, 10))
105+
})
106+
107+
test('createdate should work with showMonthYearPicker', () => {
108+
expect(createDate('12/2020', true)).toEqual(new Date(2020, 11, 1))
109+
expect(createDate('12-2020', true)).toEqual(new Date(2020, 11, 1))
110+
expect(createDate('2020/12', true)).toEqual(new Date(2020, 11, 1))
111+
})
90112
})

packages/ui/src/components/DateInput/__tests__/index.test.tsx

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import { DateInput } from '..'
99

1010
tk.freeze(new Date(1_609_503_120_000))
1111

12+
const DECEMBER = /December/i
13+
const YEAR = /1995/i
14+
1215
describe('dateInput', () => {
1316
test('renders correctly with default props', () => {
1417
const { asFragment } = renderWithTheme(
@@ -203,7 +206,7 @@ describe('dateInput', () => {
203206
const calendar = screen.getByRole('dialog')
204207
expect(calendar).toBeVisible()
205208

206-
const visibleMonth = screen.getByText(/December/i)
209+
const visibleMonth = screen.getByText(DECEMBER)
207210
await userEvent.click(screen.getByTestId('previous-month'))
208211
expect(visibleMonth.textContent).toContain('November')
209212

@@ -230,7 +233,7 @@ describe('dateInput', () => {
230233
const calendar = screen.getByRole('dialog')
231234
expect(calendar).toBeVisible()
232235

233-
const visibleMonth = screen.getByText(/1995/i)
236+
const visibleMonth = screen.getByText(YEAR)
234237
await userEvent.click(screen.getByTestId('previous-month'))
235238
expect(visibleMonth.textContent).toContain('1994')
236239

@@ -257,20 +260,20 @@ describe('dateInput', () => {
257260
await userEvent.click(input)
258261

259262
await userEvent.click(screen.getByText('15'))
260-
expect(input.value).toBe('12/15/1995')
263+
expect(input.value).toBe('15/12/1995')
261264

262265
await userEvent.click(input)
263266

264267
const dayFromLastMonth = screen.getAllByText('30')[0] // the first element in this array is from previous month
265268
await userEvent.click(dayFromLastMonth)
266269

267270
await userEvent.click(input)
268-
expect(input.value).toBe('11/30/1995')
271+
expect(input.value).toBe('30/11/1995')
269272

270273
await userEvent.click(input)
271274
const dayFromNextMonth = screen.getAllByText('1')[1]
272275
await userEvent.click(dayFromNextMonth)
273-
expect(input.value).toBe('12/01/1995')
276+
expect(input.value).toBe('01/12/1995')
274277
})
275278

276279
test('handle correctly click on date range', async () => {
@@ -290,21 +293,21 @@ describe('dateInput', () => {
290293
expect(calendar).toBeVisible()
291294

292295
await userEvent.click(screen.getByText('15'))
293-
expect(input.value).toBe('02/15/1995 - ')
296+
expect(input.value).toBe('15/02/1995 - ')
294297
const day = screen.getByText('27')
295298

296299
await userEvent.hover(day)
297300
await userEvent.unhover(day)
298301

299302
await userEvent.click(day)
300-
expect(input.value).toBe('02/15/1995 - 02/27/1995')
303+
expect(input.value).toBe('15/02/1995 - 27/02/1995')
301304

302305
await userEvent.click(input)
303306
await userEvent.click(screen.getByText('31'))
304-
expect(input.value).toBe('01/31/1995 - ')
307+
expect(input.value).toBe('31/01/1995 - ')
305308

306309
await userEvent.click(screen.getByText('20'))
307-
expect(input.value).toBe('01/20/1995 - 01/31/1995')
310+
expect(input.value).toBe('20/01/1995 - 31/01/1995')
308311
})
309312

310313
test('render correctly with showMonthYearPicker with excluded months', async () => {
@@ -408,7 +411,7 @@ describe('dateInput', () => {
408411
const input = screen.getByPlaceholderText<HTMLInputElement>('YYYY-MM-DD')
409412
await userEvent.click(input)
410413

411-
await userEvent.type(input, '08/21/1995')
414+
await userEvent.type(input, '21/08/1995')
412415
input.blur()
413416
expect(mockOnChange).toBeCalled()
414417
expect(screen.getByText('August', { exact: false })).toBeInTheDocument()
@@ -428,7 +431,7 @@ describe('dateInput', () => {
428431
const input = screen.getByPlaceholderText<HTMLInputElement>('YYYY-MM-DD')
429432
await userEvent.click(input)
430433

431-
await userEvent.type(input, '08/21/1995')
434+
await userEvent.type(input, '21/08/1995')
432435
input.blur()
433436
expect(mockOnChange).toBeCalled()
434437
expect(screen.getByText('August', { exact: false })).toBeInTheDocument()

packages/ui/src/components/DateInput/helpers.ts

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
const SPLIT_REGEX = /\D+/
2+
const DATE_SEPARATOR_REGEX = /[^a-zA-Z0-9]+/
13
// First day of the month for a given year
24
export const getMonthFirstDay = (month: number, year: number) => {
35
const firstDay = new Date(year, month - 1, 1).getDay()
@@ -43,8 +45,8 @@ const getDateISO = (showMonthYearPicker: boolean, date?: Date) => {
4345
}
4446

4547
return [
46-
addZero(date.getMonth() + 1),
4748
addZero(date.getDate()),
49+
addZero(date.getMonth() + 1),
4850
date.getFullYear(),
4951
].join('/')
5052
}
@@ -84,25 +86,49 @@ export const formatValue = (
8486
return undefined
8587
}
8688

87-
export const createDate = (value: string, showMonthYearPicker: boolean) => {
89+
const returnValidDate = (
90+
computedDate: Date,
91+
minDate?: Date,
92+
maxDate?: Date,
93+
) => {
94+
const isValidDate = !!computedDate.getTime()
95+
96+
const isTooSoon = isValidDate && minDate && computedDate < minDate
97+
const isTooLate = isValidDate && maxDate && computedDate > maxDate
98+
99+
if (isTooLate) {
100+
return maxDate
101+
}
102+
if (isTooSoon) {
103+
return minDate
104+
}
105+
106+
return isValidDate ? computedDate : null
107+
}
108+
109+
export const createDate = (
110+
value: string,
111+
showMonthYearPicker: boolean,
112+
minDate?: Date,
113+
maxDate?: Date,
114+
) => {
88115
if (showMonthYearPicker) {
89116
// Force YYYY/MM (since MM/YYYY not recognised as a date in typescript)
90-
// oxlint-disable: to fix
91-
const res = value.split(/\D+/).map(val => Number.parseInt(val, 10))
117+
const res = value.split(SPLIT_REGEX).map(val => Number.parseInt(val, 10))
92118
const year =
93119
Math.max(...res) < 100 ? Math.max(...res) + 2000 : Math.max(...res) // MM/YY should be seen as MM/20YY instead of MM/19YY
94120

95121
const month = Math.min(...res) - 1
96122
const computedDate = new Date(year, month)
97-
const isValidDate = !!computedDate.getTime()
98123

99-
return isValidDate ? computedDate : null
124+
return returnValidDate(computedDate, minDate, maxDate)
100125
}
101126

102-
const computedDate = new Date(value)
103-
const isValidDate = !!computedDate.getTime()
127+
// Cannot simply use new Date(value) since its base format is MM/DD/YYYY whereas the component uses DD/MM/YYYY
128+
const [day, month, year] = value.split(DATE_SEPARATOR_REGEX)
129+
const computedDate = new Date(`${month} ${day} ${year}`)
104130

105-
return isValidDate ? computedDate : null
131+
return returnValidDate(computedDate, minDate, maxDate)
106132
}
107133

108134
export const createDateRange = (

packages/ui/src/components/DateInput/index.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,12 @@ export const DateInput = <IsRange extends undefined | boolean>({
261261
setYearToShow(computedNewRange[0].getFullYear())
262262
}
263263
} else {
264-
const computedDate = createDate(newValue, showMonthYearPicker)
264+
const computedDate = createDate(
265+
newValue,
266+
showMonthYearPicker,
267+
minDate,
268+
maxDate,
269+
)
265270

266271
if (computedDate) {
267272
setMonthToShow(computedDate.getMonth() + 1)
@@ -287,7 +292,12 @@ export const DateInput = <IsRange extends undefined | boolean>({
287292
) => void
288293
)?.(computedNewRange, undefined)
289294
} else {
290-
const computedDate = createDate(inputValue, showMonthYearPicker)
295+
const computedDate = createDate(
296+
inputValue,
297+
showMonthYearPicker,
298+
minDate,
299+
maxDate,
300+
)
291301
;(
292302
onChange as (date: Date | null, event?: React.SyntheticEvent) => void
293303
)?.(computedDate, undefined)

0 commit comments

Comments
 (0)