From ca4e75f5f78a902ac63e6c4b83d4d5dbc9c8f84a Mon Sep 17 00:00:00 2001 From: shireenmne Date: Sun, 9 Aug 2026 13:06:47 -0400 Subject: [PATCH 1/3] fix: correct year segment backspace behavior --- .../lib/bits/date-field/date-field.svelte.ts | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/bits-ui/src/lib/bits/date-field/date-field.svelte.ts b/packages/bits-ui/src/lib/bits/date-field/date-field.svelte.ts index 1da2a23ea..3785c5215 100644 --- a/packages/bits-ui/src/lib/bits/date-field/date-field.svelte.ts +++ b/packages/bits-ui/src/lib/bits/date-field/date-field.svelte.ts @@ -211,6 +211,7 @@ export class DateFieldRootState { descriptionNode = $state(null); validationNode = $state(null); states = initSegmentStates(); + #segmentClearedValue = false; dayPeriodNode = $state(null); rangeRoot: DateRangeFieldRootState | undefined = undefined; name = $state(""); @@ -311,6 +312,10 @@ export class DateFieldRootState { $effect(() => { if (this.value.current === undefined) { + if (this.#segmentClearedValue) { + this.#segmentClearedValue = false; + return; + } this.segmentValues = initializeSegmentValues(this.inferredGranularity); } }); @@ -680,6 +685,7 @@ export class DateFieldRootState { }) ); } else { + this.#segmentClearedValue = true; this.setValue(undefined); this.segmentValues = newSegmentValues; } @@ -1234,6 +1240,9 @@ class DateFieldYearSegmentState extends BaseNumericSegmentState { str.length <= 4 ) { this.announcer.announce(mergedInt); + if (str.length === 4) { + moveToNext = true; + } return str; } @@ -1253,7 +1262,11 @@ class DateFieldYearSegmentState extends BaseNumericSegmentState { return mergedIntStr; }); - if (this.#pressedKeys.length === 4 || this.#pressedKeys.length === this.#backspaceCount) { + if (this.#pressedKeys.length === 4) { + moveToNext = true; + } + + if (this.#backspaceCount > 0 && this.#pressedKeys.length === this.#backspaceCount) { moveToNext = true; } @@ -1280,10 +1293,9 @@ class DateFieldYearSegmentState extends BaseNumericSegmentState { return null; } const next = str.slice(0, -1); - this.announcer.announce(next); - - return `${next}`; - }); + this.announcer.announce(Number.parseInt(next)); + return next; + }); if (moveToPrev) { moveToPrevSegment(e, this.root.getFieldNode()); From 1886833f445fc4541ea2dd4b122bbbdfb430755b Mon Sep 17 00:00:00 2001 From: shireenmne Date: Mon, 10 Aug 2026 10:19:33 -0400 Subject: [PATCH 2/3] adds a regression test confirming day/month segments are unaffected by year backspacing. --- tests/src/tests/date-field/date-field.browser.test.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/src/tests/date-field/date-field.browser.test.ts b/tests/src/tests/date-field/date-field.browser.test.ts index 616d5de16..4ba726aba 100644 --- a/tests/src/tests/date-field/date-field.browser.test.ts +++ b/tests/src/tests/date-field/date-field.browser.test.ts @@ -1103,6 +1103,14 @@ describe("date field", () => { await userEvent.keyboard(kbd.ARROW_DOWN); await expect.element(hour).toHaveTextContent("19"); }); + + it("should not affect day/month segments when backspacing the year", async () => { + const t = setup({ value: new CalendarDate(2023, 10, 12) }); + await t.year.click(); + await userEvent.keyboard(kbd.BACKSPACE); + await expect.element(t.day).toHaveTextContent("12"); + await expect.element(t.month).toHaveTextContent("10"); + }); }); /** From 9d0323b7c23f859746a5dbe8a42a70954b272491 Mon Sep 17 00:00:00 2001 From: Hunter Johnston Date: Wed, 19 Aug 2026 13:17:29 -0400 Subject: [PATCH 3/3] fix(date-field): keep focus while correcting year - Preserve year-segment focus after backspace correction - Add browser coverage for first-digit correction --- .changeset/fuzzy-years-focus.md | 5 +++++ .../src/lib/bits/date-field/date-field.svelte.ts | 8 ++------ .../tests/date-field/date-field.browser.test.ts | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 6 deletions(-) create mode 100644 .changeset/fuzzy-years-focus.md diff --git a/.changeset/fuzzy-years-focus.md b/.changeset/fuzzy-years-focus.md new file mode 100644 index 000000000..e13834085 --- /dev/null +++ b/.changeset/fuzzy-years-focus.md @@ -0,0 +1,5 @@ +--- +"bits-ui": patch +--- + +fix(DateField): keep focus on the year while correcting its first digit diff --git a/packages/bits-ui/src/lib/bits/date-field/date-field.svelte.ts b/packages/bits-ui/src/lib/bits/date-field/date-field.svelte.ts index 3785c5215..1f7e7159a 100644 --- a/packages/bits-ui/src/lib/bits/date-field/date-field.svelte.ts +++ b/packages/bits-ui/src/lib/bits/date-field/date-field.svelte.ts @@ -1226,7 +1226,7 @@ class DateFieldYearSegmentState extends BaseNumericSegmentState { if (prev === null) { this.announcer.announce(num); - return `000${num}`; + return `${num}`; } const str = prev.toString() + num.toString(); @@ -1266,10 +1266,6 @@ class DateFieldYearSegmentState extends BaseNumericSegmentState { moveToNext = true; } - if (this.#backspaceCount > 0 && this.#pressedKeys.length === this.#backspaceCount) { - moveToNext = true; - } - if (moveToNext) { moveToNextSegment(e, this.root.getFieldNode()); } @@ -1295,7 +1291,7 @@ class DateFieldYearSegmentState extends BaseNumericSegmentState { const next = str.slice(0, -1); this.announcer.announce(Number.parseInt(next)); return next; - }); + }); if (moveToPrev) { moveToPrevSegment(e, this.root.getFieldNode()); diff --git a/tests/src/tests/date-field/date-field.browser.test.ts b/tests/src/tests/date-field/date-field.browser.test.ts index 4ba726aba..f35d1f4f7 100644 --- a/tests/src/tests/date-field/date-field.browser.test.ts +++ b/tests/src/tests/date-field/date-field.browser.test.ts @@ -650,6 +650,20 @@ describe("date field", () => { await expect.element(hour).toHaveFocus(); }); + it("should keep focus on the year after correcting its first digit", async () => { + const t = setup({ granularity: "hour" }); + const { getHour } = getTimeSegments(page.getByTestId); + + await t.year.click(); + await userEvent.keyboard("1"); + await userEvent.keyboard(kbd.BACKSPACE); + await userEvent.keyboard("2"); + + await expect.element(t.year).toHaveTextContent("2"); + await expect.element(t.year).toHaveFocus(); + await expect.element(getHour()).not.toHaveFocus(); + }); + it("should allow going from 12PM -> 12AM without changing the display hour to 0", async () => { setup({ value: new CalendarDateTime(2023, 10, 12, 12, 30, 0, 0),