Skip to content

Commit 6523c01

Browse files
committed
fix: date format
1 parent f137b65 commit 6523c01

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
@@ -7,6 +7,6 @@ export const Template: StoryFn<typeof DateInput> = props => (
77

88
Template.args = {
99
label: 'Date Input',
10-
placeholder: 'MM-DD-YYYY',
10+
placeholder: 'DD-MM-YYYY',
1111
required: true,
1212
}

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 styles_flexWrap_nowrap_xxsmall__toi52u31 styles_justifyContent_normal_xxsmall__toi52u5v"

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

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { describe, expect, test } from 'vitest'
22
import {
33
addZero,
4+
createDate,
45
formatValue,
56
getMonthFirstDay,
67
getNextMonth,
@@ -64,14 +65,14 @@ describe('helper functions dateInput', () => {
6465
})
6566

6667
test('formatValue should work with default formatting', () => {
67-
expect(formatValue(date, null, false, false)).toBe('11/20/2000')
68+
expect(formatValue(date, null, false, false)).toBe('20/11/2000')
6869
expect(formatValue(date, null, true, false)).toBe('11/2000')
69-
expect(formatValue(date, null, false, true)).toBe('11/20/2000')
70+
expect(formatValue(date, null, false, true)).toBe('20/11/2000')
7071

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

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

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

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

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

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

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

@@ -229,7 +232,7 @@ describe('dateInput', () => {
229232
const calendar = screen.getByRole('dialog')
230233
expect(calendar).toBeVisible()
231234

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

@@ -256,20 +259,20 @@ describe('dateInput', () => {
256259
await userEvent.click(input)
257260

258261
await userEvent.click(screen.getByText('15'))
259-
expect(input.value).toBe('12/15/1995')
262+
expect(input.value).toBe('15/12/1995')
260263

261264
await userEvent.click(input)
262265

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

266269
await userEvent.click(input)
267-
expect(input.value).toBe('11/30/1995')
270+
expect(input.value).toBe('30/11/1995')
268271

269272
await userEvent.click(input)
270273
const dayFromNextMonth = screen.getAllByText('1')[1]
271274
await userEvent.click(dayFromNextMonth)
272-
expect(input.value).toBe('12/01/1995')
275+
expect(input.value).toBe('01/12/1995')
273276
})
274277

275278
test('handle correctly click on date range', async () => {
@@ -289,21 +292,21 @@ describe('dateInput', () => {
289292
expect(calendar).toBeVisible()
290293

291294
await userEvent.click(screen.getByText('15'))
292-
expect(input.value).toBe('02/15/1995 - ')
295+
expect(input.value).toBe('15/02/1995 - ')
293296
const day = screen.getByText('27')
294297

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

298301
await userEvent.click(day)
299-
expect(input.value).toBe('02/15/1995 - 02/27/1995')
302+
expect(input.value).toBe('15/02/1995 - 27/02/1995')
300303

301304
await userEvent.click(input)
302305
await userEvent.click(screen.getByText('31'))
303-
expect(input.value).toBe('01/31/1995 - ')
306+
expect(input.value).toBe('31/01/1995 - ')
304307

305308
await userEvent.click(screen.getByText('20'))
306-
expect(input.value).toBe('01/20/1995 - 01/31/1995')
309+
expect(input.value).toBe('20/01/1995 - 31/01/1995')
307310
})
308311

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

410-
await userEvent.type(input, '08/21/1995')
413+
await userEvent.type(input, '21/08/1995')
411414
input.blur()
412415
expect(mockOnChange).toBeCalled()
413416
expect(screen.getByText('August', { exact: false })).toBeInTheDocument()
@@ -427,7 +430,7 @@ describe('dateInput', () => {
427430
const input = screen.getByPlaceholderText<HTMLInputElement>('YYYY-MM-DD')
428431
await userEvent.click(input)
429432

430-
await userEvent.type(input, '08/21/1995')
433+
await userEvent.type(input, '21/08/1995')
431434
input.blur()
432435
expect(mockOnChange).toBeCalled()
433436
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-
// biome-ignore lint/performance/useTopLevelRegex: 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
@@ -258,7 +258,12 @@ export const DateInput = <IsRange extends undefined | boolean>({
258258
setYearToShow(computedNewRange[0].getFullYear())
259259
}
260260
} else {
261-
const computedDate = createDate(newValue, showMonthYearPicker)
261+
const computedDate = createDate(
262+
newValue,
263+
showMonthYearPicker,
264+
minDate,
265+
maxDate,
266+
)
262267

263268
if (computedDate) {
264269
setMonthToShow(computedDate.getMonth() + 1)
@@ -284,7 +289,12 @@ export const DateInput = <IsRange extends undefined | boolean>({
284289
) => void
285290
)?.(computedNewRange, undefined)
286291
} else {
287-
const computedDate = createDate(inputValue, showMonthYearPicker)
292+
const computedDate = createDate(
293+
inputValue,
294+
showMonthYearPicker,
295+
minDate,
296+
maxDate,
297+
)
288298
;(
289299
onChange as (date: Date | null, event?: React.SyntheticEvent) => void
290300
)?.(computedDate, undefined)

0 commit comments

Comments
 (0)