From 78f0d5e25f3c704f5cd373818de6f5c294144638 Mon Sep 17 00:00:00 2001 From: Eli Morris-Heft Date: Mon, 22 Apr 2024 13:25:14 -0400 Subject: [PATCH] Fixed #5545: Restore functionality for InputNumber on Android --- .../lib/inputnumber/InputNumber.spec.js | 43 ++++++++++++++++--- components/lib/inputnumber/InputNumber.vue | 37 ++++++++++++++++ 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/components/lib/inputnumber/InputNumber.spec.js b/components/lib/inputnumber/InputNumber.spec.js index 080dab187f5..5eaf40e5ffa 100644 --- a/components/lib/inputnumber/InputNumber.spec.js +++ b/components/lib/inputnumber/InputNumber.spec.js @@ -1,4 +1,6 @@ +import { afterEach, vi } from 'vitest'; import { mount } from '@vue/test-utils'; +import { DomHandler } from 'primevue/utils'; import InputNumber from './InputNumber.vue'; describe('InputNumber.vue', () => { @@ -12,6 +14,10 @@ describe('InputNumber.vue', () => { }); }); + afterEach(() => { + vi.restoreAllMocks(); + }); + it('is exist', () => { expect(wrapper.find('.p-inputnumber.p-component').exists()).toBe(true); expect(wrapper.find('input.p-inputnumber-input').exists()).toBe(true); @@ -41,18 +47,45 @@ describe('InputNumber.vue', () => { expect(wrapper.find('input.p-inputnumber-input').attributes()['aria-valuenow']).toBe('12'); }); - it('is keypress called when pressed a number', async () => { - wrapper.find('input.p-inputnumber-input').element.setSelectionRange(2, 2); + it('is keydown called when pressed a number', async () => { + // set the cursor to the end of the input field + wrapper.find('input.p-inputnumber-input').element.setSelectionRange(1, 1); + + await wrapper.vm.onInputKeyDown({ code: 'Digit1', key: '1', target: { value: '1' }, preventDefault: () => {} }); + + expect(wrapper.emitted().input[0][0].value).toBe(11); + }); + + it('calls the keypress handler when a number is pressed on android', async () => { + // pretend we're on Android + vi.spyOn(DomHandler, 'isAndroid').mockReturnValue(true); + + // set the cursor to the end of the input field + wrapper.find('input.p-inputnumber-input').element.setSelectionRange(1, 1); - await wrapper.vm.onInputKeyPress({ which: 49, preventDefault: () => {} }); + await wrapper.vm.onInputAndroidKey({ which: 49, preventDefault: () => {} }); expect(wrapper.emitted().input[0][0].value).toBe(11); }); - it('is keypress called when pressed minus', async () => { + it('is keydown called when pressed minus', async () => { + // set the cursor to the start of the input field + wrapper.find('input.p-inputnumber-input').element.setSelectionRange(0, 0); + + // await wrapper.vm.onInputKeyDown({ keyCode: 45, preventDefault: () => {} }); + await wrapper.vm.onInputKeyDown({ code: 'Minus', key: '-', target: { value: '1' }, preventDefault: () => {} }); + + expect(wrapper.emitted().input[0][0].value).toBe(-1); + }); + + it('calls the keypress handler when minus is pressed on android', async () => { + // pretend we're on Android + vi.spyOn(DomHandler, 'isAndroid').mockReturnValue(true); + + // set the cursor to the start of the input field wrapper.find('input.p-inputnumber-input').element.setSelectionRange(0, 0); - await wrapper.vm.onInputKeyPress({ keyCode: 45, preventDefault: () => {} }); + await wrapper.vm.onInputAndroidKey({ which: 45, preventDefault: () => {} }); expect(wrapper.emitted().input[0][0].value).toBe(-1); }); diff --git a/components/lib/inputnumber/InputNumber.vue b/components/lib/inputnumber/InputNumber.vue index 2a63df9347e..3314c67a045 100755 --- a/components/lib/inputnumber/InputNumber.vue +++ b/components/lib/inputnumber/InputNumber.vue @@ -18,6 +18,7 @@ :aria-invalid="invalid || undefined" @input="onUserInput" @keydown="onInputKeyDown" + @keypress="onInputAndroidKey" @paste="onPaste" @click="onInputClick" @focus="onInputFocus" @@ -371,6 +372,37 @@ export default { this.isSpecialChar = false; }, + onInputAndroidKey(event) { + if (!DomHandler.isAndroid() || this.disabled || this.readOnly) { + return; + } + + if (this.onKeyUp) { + this.onKeyUp(event); + + // do not continue if the user defined event wants to prevent + if (event.defaultPrevented) { + return; + } + } + + const code = event.which || event.keyCode; + + if (code !== 13) { + // to submit a form + event.preventDefault(); + } + + const char = String.fromCharCode(code); + const _isDecimalSign = this.isDecimalSign(char); + const _isMinusSign = this.isMinusSign(char); + + if ((48 <= code && code <= 57) || _isMinusSign || _isDecimalSign) { + this.insert(event, char, { isDecimalSign: _isDecimalSign, isMinusSign: _isMinusSign }); + } else { + this.updateValue(event, event.target.value, null, 'delete-single'); + } + }, onInputKeyDown(event) { if (this.readonly) { return; @@ -384,6 +416,11 @@ export default { this.lastValue = event.target.value; + // Android is handled specially in onInputAndroidKey + if (DomHandler.isAndroid()) { + return; + } + let selectionStart = event.target.selectionStart; let selectionEnd = event.target.selectionEnd; let inputValue = event.target.value;