fix(ui): clear miliseconds in date fields unless theyre explicitly provided in the display format (#12650)
Fixes https://github.com/payloadcms/payload/issues/12532 Normally we clear any values when picking a date such that your hour, minutes and seconds are normalised to 0 unless specified. Equally when you specify a time we will normalise seconds so that only minutes are relevant as configured. Miliseconds were never removed from the actual date value and whatever milisecond the editor was in was that value that was being added. There's this [abandoned issue](https://github.com/Hacker0x01/react-datepicker/issues/1991) from the UI library `react-datepicker` as it's not something configurable. This fixes that problem by making sure that miliseconds are always 0 unless the `displayFormat` includes `SSS` as an intention to show and customise them. This also caused [issues with scheduled jobs](https://github.com/payloadcms/payload/issues/12566) if things were slightly out of order or not being scheduled in the expected time interval.
This commit is contained in:
@@ -111,6 +111,70 @@ describe('Date', () => {
|
||||
await expect(dateField).toHaveValue('')
|
||||
})
|
||||
|
||||
test('should clear miliseconds from dates with time', async () => {
|
||||
await page.goto(url.create)
|
||||
const dateField = page.locator('#field-default input')
|
||||
await expect(dateField).toBeVisible()
|
||||
// Fill in required fields, this is just to make sure saving is possible
|
||||
await dateField.fill('02/07/2023')
|
||||
const dateWithTz = page.locator('#field-dayAndTimeWithTimezone .react-datepicker-wrapper input')
|
||||
|
||||
await dateWithTz.fill('08/12/2027 10:00 AM')
|
||||
|
||||
const dropdownControlSelector = `#field-dayAndTimeWithTimezone .rs__control`
|
||||
const timezoneOptionSelector = `#field-dayAndTimeWithTimezone .rs__menu .rs__option:has-text("London")`
|
||||
|
||||
await page.click(dropdownControlSelector)
|
||||
await page.click(timezoneOptionSelector)
|
||||
|
||||
// Test the time field
|
||||
const timeField = page.locator('#field-timeOnly input')
|
||||
await timeField.fill('08/12/2027 10:00:00.123 AM')
|
||||
|
||||
await saveDocAndAssert(page)
|
||||
|
||||
const id = page.url().split('/').pop()
|
||||
|
||||
const { doc } = await client.findByID({ id: id!, auth: true, slug: 'date-fields' })
|
||||
|
||||
await expect(() => {
|
||||
// Ensure that the time field does not contain milliseconds
|
||||
expect(doc?.timeOnly).toContain('00:00.000Z')
|
||||
}).toPass()
|
||||
})
|
||||
|
||||
test("should keep miliseconds when they're provided in the date format", async () => {
|
||||
await page.goto(url.create)
|
||||
const dateField = page.locator('#field-default input')
|
||||
await expect(dateField).toBeVisible()
|
||||
// Fill in required fields, this is just to make sure saving is possible
|
||||
await dateField.fill('02/07/2023')
|
||||
const dateWithTz = page.locator('#field-dayAndTimeWithTimezone .react-datepicker-wrapper input')
|
||||
|
||||
await dateWithTz.fill('08/12/2027 10:00 AM')
|
||||
|
||||
const dropdownControlSelector = `#field-dayAndTimeWithTimezone .rs__control`
|
||||
const timezoneOptionSelector = `#field-dayAndTimeWithTimezone .rs__menu .rs__option:has-text("London")`
|
||||
|
||||
await page.click(dropdownControlSelector)
|
||||
await page.click(timezoneOptionSelector)
|
||||
|
||||
// Test the time field
|
||||
const timeField = page.locator('#field-timeOnlyWithMiliseconds input')
|
||||
await timeField.fill('6:00.00.625 PM')
|
||||
|
||||
await saveDocAndAssert(page)
|
||||
|
||||
const id = page.url().split('/').pop()
|
||||
|
||||
const { doc } = await client.findByID({ id: id!, auth: true, slug: 'date-fields' })
|
||||
|
||||
await expect(() => {
|
||||
// Ensure that the time with miliseconds field contains the exact miliseconds specified
|
||||
expect(doc?.timeOnlyWithMiliseconds).toContain('625Z')
|
||||
}).toPass()
|
||||
})
|
||||
|
||||
describe('localized dates', () => {
|
||||
describe('EST', () => {
|
||||
test.use({
|
||||
|
||||
@@ -24,6 +24,16 @@ const DateFields: CollectionConfig = {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'timeOnlyWithMiliseconds',
|
||||
type: 'date',
|
||||
admin: {
|
||||
date: {
|
||||
pickerAppearance: 'timeOnly',
|
||||
displayFormat: 'h:mm.ss.SSS aa',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'timeOnlyWithCustomFormat',
|
||||
type: 'date',
|
||||
|
||||
@@ -903,6 +903,7 @@ export interface DateField {
|
||||
id: string;
|
||||
default: string;
|
||||
timeOnly?: string | null;
|
||||
timeOnlyWithMiliseconds?: string | null;
|
||||
timeOnlyWithCustomFormat?: string | null;
|
||||
dayOnly?: string | null;
|
||||
dayAndTime?: string | null;
|
||||
@@ -2486,6 +2487,7 @@ export interface CustomRowIdSelect<T extends boolean = true> {
|
||||
export interface DateFieldsSelect<T extends boolean = true> {
|
||||
default?: T;
|
||||
timeOnly?: T;
|
||||
timeOnlyWithMiliseconds?: T;
|
||||
timeOnlyWithCustomFormat?: T;
|
||||
dayOnly?: T;
|
||||
dayAndTime?: T;
|
||||
|
||||
Reference in New Issue
Block a user