diff --git a/src/PickerInput/Selector/Input.tsx b/src/PickerInput/Selector/Input.tsx index 38a1fbe2a..418113c89 100644 --- a/src/PickerInput/Selector/Input.tsx +++ b/src/PickerInput/Selector/Input.tsx @@ -33,6 +33,7 @@ export interface InputProps extends Omit boolean; onChange: (value: string) => void; onSubmit: VoidFunction; /** Meaning current is from the hover cell getting the placeholder text */ @@ -65,6 +66,7 @@ const Input = React.forwardRef((props, ref) => { preserveInvalidOnBlur = false, invalid, clearIcon, + preserveInputOnValueChange, // Pass to input ...restProps } = props; @@ -81,16 +83,28 @@ const Input = React.forwardRef((props, ref) => { // ======================== Value ========================= const [focused, setFocused] = React.useState(false); const [internalInputValue, setInputValue] = React.useState(value); + const inputValueRef = React.useRef(value); const [focusCellText, setFocusCellText] = React.useState(''); const [focusCellIndex, setFocusCellIndex] = React.useState(null); const [forceSelectionSyncMark, forceSelectionSync] = React.useState(null); const inputValue = internalInputValue || ''; + const updateInputValue = useEvent((nextValue: string) => { + inputValueRef.current = nextValue; + setInputValue(nextValue); + }); + + const shouldPreserveInput = useEvent( + (nextValue: string) => + (focused || active) && preserveInputOnValueChange?.(inputValueRef.current || '', nextValue), + ); // Sync value if needed React.useEffect(() => { - setInputValue(value); - }, [value]); + if (!shouldPreserveInput(value)) { + updateInputValue(value); + } + }, [value, shouldPreserveInput, updateInputValue]); // ========================= Refs ========================= const holderRef = React.useRef(null); @@ -133,10 +147,11 @@ const Input = React.forwardRef((props, ref) => { * Triggered by paste, keyDown and focus to show format */ const triggerInputChange = useEvent((text: string) => { + inputValueRef.current = text; if (validateFormat(text)) { onChange(text); } - setInputValue(text); + updateInputValue(text); onModify(text); }); @@ -147,7 +162,7 @@ const Input = React.forwardRef((props, ref) => { const text = event.target.value; onModify(text); - setInputValue(text); + updateInputValue(text); onChange(text); } }; @@ -211,7 +226,7 @@ const Input = React.forwardRef((props, ref) => { // Check if blur need reset input value useLockEffect(active, () => { if (!active && !preserveInvalidOnBlur) { - setInputValue(value); + updateInputValue(value); } }); diff --git a/src/PickerInput/Selector/hooks/useInputProps.ts b/src/PickerInput/Selector/hooks/useInputProps.ts index 605551afb..a9fc8005a 100644 --- a/src/PickerInput/Selector/hooks/useInputProps.ts +++ b/src/PickerInput/Selector/hooks/useInputProps.ts @@ -151,6 +151,11 @@ export default function useInputProps( value: getProp(valueTexts) || '', + preserveInputOnValueChange: (text: string, nextValue: string) => { + const parsed = validateFormat(text); + return !!parsed && getText(parsed) === nextValue; + }, + invalid: getProp(invalid), placeholder: getProp(placeholder), diff --git a/tests/picker.spec.tsx b/tests/picker.spec.tsx index b33fa2fa4..f02d37579 100644 --- a/tests/picker.spec.tsx +++ b/tests/picker.spec.tsx @@ -920,6 +920,39 @@ describe('Picker.Basic', () => { expect(document.querySelector('input').value).toEqual('20000101'); }); + it('allows typing a four-digit year when a shorter format also matches', () => { + const onChange = jest.fn(); + const { container, rerender } = render( + , + ); + const input = container.querySelector('input'); + + openPicker(container); + const text = '01-12-2024'; + for (let index = 1; index <= text.length; index += 1) { + fireEvent.change(input, { target: { value: text.slice(0, index) } }); + + if (index === '01-12-20'.length) { + expect(input).toHaveValue('01-12-20'); + } + } + + expect(input).toHaveValue('01-12-2024'); + fireEvent.keyDown(input, { key: 'Enter' }); + expect(onChange).toHaveBeenCalledWith(expect.anything(), '01-12-2024'); + + triggerFocus(input); + fireEvent.change(input, { target: { value: '01-12-20' } }); + rerender( + , + ); + expect(input).toHaveValue('09-09-1999'); + }); + it('custom format', () => { const { container } = render(