From bbad23aaf53866251eadbb743addc7e871df3e42 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 22 Sep 2026 10:36:44 +0100 Subject: [PATCH 1/3] bump reka-ui to fix the day period of zoned dates Co-Authored-By: Claude Fable 5.1 --- package-lock.json | 47 ++----------------- package.json | 2 +- .../js/tests/components/ui/DatePicker.test.js | 40 ++++++++++++++++ 3 files changed, 45 insertions(+), 44 deletions(-) create mode 100644 resources/js/tests/components/ui/DatePicker.test.js diff --git a/package-lock.json b/package-lock.json index 875687cae0c..12f2d5b46fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -70,7 +70,7 @@ "pusher-js": "^8.4.0-rc2", "qs": "^6.15.2", "read-time-estimate": "0.0.3", - "reka-ui": "^2.9.2", + "reka-ui": "^2.10.5", "resize-observer-polyfill": "^1.5.1", "semver": "^7.7.1", "speakingurl": "^14.0.1", @@ -1221,9 +1221,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1244,9 +1241,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1267,9 +1261,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1290,9 +1281,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1313,9 +1301,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1559,9 +1544,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1579,9 +1561,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1599,9 +1578,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1619,9 +1595,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1639,9 +1612,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1659,9 +1629,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1679,9 +1646,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -1699,9 +1663,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -7760,9 +7721,9 @@ } }, "node_modules/reka-ui": { - "version": "2.9.5", - "resolved": "https://registry.npmjs.org/reka-ui/-/reka-ui-2.9.5.tgz", - "integrity": "sha512-6cZGIMgEeslpFLJ7IihaCSMPp1cJgl2eDkZ2vBMdl+HPUVBaV/iDPMWu3abT2KUkj1lir+oyHq5KelOTT9OheQ==", + "version": "2.10.5", + "resolved": "https://registry.npmjs.org/reka-ui/-/reka-ui-2.10.5.tgz", + "integrity": "sha512-/8o0y9BThE0hcHKFg+3770tWcJh3qKdOEFxLOPb9HIajXeFoH/J530cZH62jUJunbqwBgpSBL9v7nB8LJ5pDXA==", "license": "MIT", "dependencies": { "@floating-ui/dom": "^1.6.13", diff --git a/package.json b/package.json index ed0bcb9b59d..b7befe44c1d 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "pusher-js": "^8.4.0-rc2", "qs": "^6.15.2", "read-time-estimate": "0.0.3", - "reka-ui": "^2.9.2", + "reka-ui": "^2.10.5", "resize-observer-polyfill": "^1.5.1", "semver": "^7.7.1", "speakingurl": "^14.0.1", diff --git a/resources/js/tests/components/ui/DatePicker.test.js b/resources/js/tests/components/ui/DatePicker.test.js new file mode 100644 index 00000000000..08f27bf91c4 --- /dev/null +++ b/resources/js/tests/components/ui/DatePicker.test.js @@ -0,0 +1,40 @@ +import { mount } from '@vue/test-utils'; +import { expect, test } from 'vitest'; +import { CalendarDateTime, toZoned } from '@internationalized/date'; +import DatePicker from '@/components/ui/DatePicker/DatePicker.vue'; +import { config } from '@api'; + +window.__ = (key) => key; + +config.initialize({ translationLocale: 'en' }); + +const makeDatePicker = (modelValue) => { + return mount(DatePicker, { + props: { modelValue, granularity: 'minute' }, + global: { + mocks: { + $date: { locale: 'en-US' }, + }, + }, + }); +}; + +test('day period reflects the zoned date rather than the browser timezone', () => { + process.env.TZ = 'America/New_York'; + + const datePicker = makeDatePicker(toZoned(new CalendarDateTime(2024, 1, 20, 15, 30), 'Europe/London')); + + expect(datePicker.get('[data-reka-date-field-segment="hour"]').text()).toBe('3'); + expect(datePicker.get('[data-reka-date-field-segment="dayPeriod"]').text()).toBe('PM'); +}); + +test('typing an hour keeps the day period of the zoned date', async () => { + process.env.TZ = 'America/New_York'; + + const modelValue = toZoned(new CalendarDateTime(2024, 1, 20, 15, 30), 'Europe/London'); + const datePicker = makeDatePicker(modelValue); + + await datePicker.get('[data-reka-date-field-segment="hour"]').trigger('keydown', { key: '5' }); + + expect(datePicker.emitted('update:modelValue')[0][0].toString()).toBe(modelValue.set({ hour: 17 }).toString()); +}); From 5b7b1c6f85d1d10b9cd508f62fcf03089b01b1ee Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 22 Sep 2026 10:36:44 +0100 Subject: [PATCH 2/3] only use the local placeholder in the range picker when there's no value Co-Authored-By: Claude Fable 5.1 --- .../ui/DateRangePicker/DateRangePicker.vue | 2 +- .../components/ui/DateRangePicker.test.js | 50 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 resources/js/tests/components/ui/DateRangePicker.test.js diff --git a/resources/js/components/ui/DateRangePicker/DateRangePicker.vue b/resources/js/components/ui/DateRangePicker/DateRangePicker.vue index 14e9e672665..5ba6850a7d9 100644 --- a/resources/js/components/ui/DateRangePicker/DateRangePicker.vue +++ b/resources/js/components/ui/DateRangePicker/DateRangePicker.vue @@ -133,7 +133,7 @@ const hoverCardDate = computed(() => { v-bind="$attrs" prevent-deselect hide-time-zone - :default-placeholder="defaultPlaceholder" + :default-placeholder="modelValue?.start ? undefined : defaultPlaceholder" close-on-select > diff --git a/resources/js/tests/components/ui/DateRangePicker.test.js b/resources/js/tests/components/ui/DateRangePicker.test.js new file mode 100644 index 00000000000..14a0347983f --- /dev/null +++ b/resources/js/tests/components/ui/DateRangePicker.test.js @@ -0,0 +1,50 @@ +import { mount } from '@vue/test-utils'; +import { expect, test } from 'vitest'; +import { CalendarDateTime, toZoned } from '@internationalized/date'; +import DateRangePicker from '@/components/ui/DateRangePicker/DateRangePicker.vue'; +import { config } from '@api'; + +window.__ = (key) => key; + +config.initialize({ translationLocale: 'en' }); + +const makeDateRangePicker = (modelValue) => { + return mount(DateRangePicker, { + props: { modelValue, granularity: 'minute' }, + global: { + mocks: { + $date: { locale: 'en-US' }, + }, + }, + }); +}; + +const segment = (type, part) => `[data-reka-date-range-field-segment-type="${type}"][data-reka-date-field-segment="${part}"]`; + +test('day periods reflect the zoned dates rather than the browser timezone', () => { + process.env.TZ = 'America/New_York'; + + const dateRangePicker = makeDateRangePicker({ + start: toZoned(new CalendarDateTime(2024, 1, 20, 15, 30), 'Europe/London'), + end: toZoned(new CalendarDateTime(2024, 1, 21, 16, 30), 'Europe/London'), + }); + + expect(dateRangePicker.get(segment('start', 'hour')).text()).toBe('3'); + expect(dateRangePicker.get(segment('start', 'dayPeriod')).text()).toBe('PM'); + expect(dateRangePicker.get(segment('end', 'hour')).text()).toBe('4'); + expect(dateRangePicker.get(segment('end', 'dayPeriod')).text()).toBe('PM'); +}); + +test('typing an hour keeps the day period of the zoned dates', async () => { + process.env.TZ = 'America/New_York'; + + const start = toZoned(new CalendarDateTime(2024, 1, 20, 15, 30), 'Europe/London'); + const end = toZoned(new CalendarDateTime(2024, 1, 21, 16, 30), 'Europe/London'); + const dateRangePicker = makeDateRangePicker({ start, end }); + + await dateRangePicker.get(segment('end', 'hour')).trigger('keydown', { key: '5' }); + + const emitted = dateRangePicker.emitted('update:modelValue')[0][0]; + expect(emitted.start.toString()).toBe(start.toString()); + expect(emitted.end.toString()).toBe(end.set({ hour: 17 }).toString()); +}); From a5f734f6079afcb0d98f83f80e7c34f11f7e3991 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Tue, 22 Sep 2026 16:36:06 -0400 Subject: [PATCH 3/3] Anchor range default-placeholder on either side of the range modelValue?.start alone left an end-only range falling back to the browser-local placeholder, reintroducing the day-period bug for that case. Co-Authored-By: Claude Sonnet 5 --- .../ui/DateRangePicker/DateRangePicker.vue | 2 +- .../js/tests/components/ui/DateRangePicker.test.js | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/resources/js/components/ui/DateRangePicker/DateRangePicker.vue b/resources/js/components/ui/DateRangePicker/DateRangePicker.vue index 5ba6850a7d9..7ff4a97ede3 100644 --- a/resources/js/components/ui/DateRangePicker/DateRangePicker.vue +++ b/resources/js/components/ui/DateRangePicker/DateRangePicker.vue @@ -133,7 +133,7 @@ const hoverCardDate = computed(() => { v-bind="$attrs" prevent-deselect hide-time-zone - :default-placeholder="modelValue?.start ? undefined : defaultPlaceholder" + :default-placeholder="(modelValue?.start ?? modelValue?.end) ? undefined : defaultPlaceholder" close-on-select > diff --git a/resources/js/tests/components/ui/DateRangePicker.test.js b/resources/js/tests/components/ui/DateRangePicker.test.js index 14a0347983f..dcb02368e44 100644 --- a/resources/js/tests/components/ui/DateRangePicker.test.js +++ b/resources/js/tests/components/ui/DateRangePicker.test.js @@ -48,3 +48,15 @@ test('typing an hour keeps the day period of the zoned dates', async () => { expect(emitted.start.toString()).toBe(start.toString()); expect(emitted.end.toString()).toBe(end.set({ hour: 17 }).toString()); }); + +test('day period reflects the zoned date rather than the browser timezone when only the end is set', () => { + process.env.TZ = 'America/New_York'; + + const dateRangePicker = makeDateRangePicker({ + start: undefined, + end: toZoned(new CalendarDateTime(2024, 1, 21, 16, 30), 'Europe/London'), + }); + + expect(dateRangePicker.get(segment('end', 'hour')).text()).toBe('4'); + expect(dateRangePicker.get(segment('end', 'dayPeriod')).text()).toBe('PM'); +});