diff --git a/packages/@react-aria/datepicker/src/useDateSegment.ts b/packages/@react-aria/datepicker/src/useDateSegment.ts index d11bd2761e7..15af0b41bf8 100644 --- a/packages/@react-aria/datepicker/src/useDateSegment.ts +++ b/packages/@react-aria/datepicker/src/useDateSegment.ts @@ -93,9 +93,13 @@ export function useDateSegment(segment: DateSegment, state: DateFieldState, ref: let parser = useMemo(() => new NumberParser(locale, {maximumFractionDigits: 0}), [locale]); let backspace = () => { + if (segment.text === segment.placeholder) { + focusManager.focusPrevious(); + } if (parser.isValidPartialNumber(segment.text) && !state.isReadOnly && !segment.isPlaceholder) { let newValue = segment.text.slice(0, -1); let parsed = parser.parse(newValue); + newValue = parsed === 0 ? '' : newValue; if (newValue.length === 0 || parsed === 0) { state.clearSegment(segment.type); } else { diff --git a/packages/react-aria-components/stories/DateField.stories.tsx b/packages/react-aria-components/stories/DateField.stories.tsx index 6cb89df3121..7abec4f2407 100644 --- a/packages/react-aria-components/stories/DateField.stories.tsx +++ b/packages/react-aria-components/stories/DateField.stories.tsx @@ -12,6 +12,7 @@ import clsx from 'clsx'; import {DateField, DateInput, DateSegment, Label} from 'react-aria-components'; +import {parseAbsoluteToLocal} from '@internationalized/date'; import React from 'react'; import styles from '../example/index.css'; @@ -20,7 +21,7 @@ export default { }; export const DateFieldExample = () => ( - + {segment => } diff --git a/packages/react-aria-components/test/DateField.test.js b/packages/react-aria-components/test/DateField.test.js index 0ca86115e78..d91a68a6c7c 100644 --- a/packages/react-aria-components/test/DateField.test.js +++ b/packages/react-aria-components/test/DateField.test.js @@ -258,4 +258,35 @@ describe('DateField', () => { expect(getDescription()).not.toContain('Constraints not satisfied'); expect(group).not.toHaveAttribute('data-invalid'); }); + + it('should focus previous segment when backspacing on an empty date segment', async () => { + let {getAllByRole} = render( + + + + {segment => } + + + ); + + let segments = getAllByRole('spinbutton'); + await user.click(segments[2]); + expect(document.activeElement).toBe(segments[2]); + + // Press backspace to delete '2024' + for (let i = 0; i < 4; i++) { + await user.keyboard('{backspace}'); + } + expect(document.activeElement).toBe(segments[2]); + await user.keyboard('{backspace}'); + expect(document.activeElement).toBe(segments[1]); + + // Press backspace to delete '31' + for (let i = 0; i < 2; i++) { + await user.keyboard('{backspace}'); + } + expect(document.activeElement).toBe(segments[1]); + await user.keyboard('{backspace}'); + expect(document.activeElement).toBe(segments[0]); + }); });