From f92a1eb17c5a30673efaf46dd2458509d1830d26 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 14:39:59 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(number-input):=20jumping=20u?= =?UTF-8?q?sing=20TAB=20is=20inconsistent=20(#1379)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Create PR for #1369 * fix numer input issues * fix numer input issues * add changesets --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Gery Hirschfeld --- .changeset/cool-pots-pump.md | 5 + .changeset/dirty-owls-share.md | 5 + .changeset/real-buses-approve.md | 5 + .../bal-number-input/bal-number-input.tsx | 7 +- .../bal-number-input.utils.spec.ts | 93 +++++++++++++++++++ .../bal-number-input.utils.ts | 46 ++++++--- .../test/bal-number-input.cy.html | 50 +++++++++- 7 files changed, 195 insertions(+), 16 deletions(-) create mode 100644 .changeset/cool-pots-pump.md create mode 100644 .changeset/dirty-owls-share.md create mode 100644 .changeset/real-buses-approve.md diff --git a/.changeset/cool-pots-pump.md b/.changeset/cool-pots-pump.md new file mode 100644 index 0000000000..7b5d54205d --- /dev/null +++ b/.changeset/cool-pots-pump.md @@ -0,0 +1,5 @@ +--- +'@baloise/ds-core': minor +--- + +**number-input**: supports select-all, copy and paste diff --git a/.changeset/dirty-owls-share.md b/.changeset/dirty-owls-share.md new file mode 100644 index 0000000000..93f35bba1c --- /dev/null +++ b/.changeset/dirty-owls-share.md @@ -0,0 +1,5 @@ +--- +'@baloise/ds-core': patch +--- + +**number-input**: tab navigation to be consistent diff --git a/.changeset/real-buses-approve.md b/.changeset/real-buses-approve.md new file mode 100644 index 0000000000..c102e9e481 --- /dev/null +++ b/.changeset/real-buses-approve.md @@ -0,0 +1,5 @@ +--- +'@baloise/ds-core': patch +--- + +**number-input**: supports autofill format diff --git a/packages/core/src/components/bal-number-input/bal-number-input.tsx b/packages/core/src/components/bal-number-input/bal-number-input.tsx index 97202ffda4..2f6a247e7e 100644 --- a/packages/core/src/components/bal-number-input/bal-number-input.tsx +++ b/packages/core/src/components/bal-number-input/bal-number-input.tsx @@ -62,6 +62,7 @@ export class NumberInput { private inputId = `bal-number-input-${numberInputIds++}` private inheritedAttributes: { [k: string]: any } = {} + private selectTimeout?: NodeJS.Timeout lastValue = '' nativeInput?: HTMLInputElement @@ -371,11 +372,12 @@ export class NumberInput private onFocus = (ev: FocusEvent) => { inputHandleFocus(this, ev) - // // restore the input with the last user value without the formatting if (this.nativeInput) { this.nativeInputValue = mapDecimalSeparator(this.lastValue || '') + clearTimeout(this.selectTimeout) + this.selectTimeout = setTimeout(() => this.nativeInput.select()) } } @@ -387,6 +389,7 @@ export class NumberInput if (this.nativeInput) { this.lastValue = toFixedNumber(this.lastValueGetter, this.decimal) this.nativeInputValue = toUserFormattedNumber(this.lastValueGetter, this.decimal, this.suffix) + this.nativeInput.value = this.nativeInputValue } this.inputValue = toNumber(this.lastValueGetter, this.decimal) @@ -403,6 +406,8 @@ export class NumberInput input && !validateKeyDown({ key: ev.key, + ctrlKey: ev.ctrlKey, + metaKey: ev.metaKey, decimal: this.decimal, newValue, oldValue, diff --git a/packages/core/src/components/bal-number-input/bal-number-input.utils.spec.ts b/packages/core/src/components/bal-number-input/bal-number-input.utils.spec.ts index 34afef5d72..5130ff4935 100644 --- a/packages/core/src/components/bal-number-input/bal-number-input.utils.spec.ts +++ b/packages/core/src/components/bal-number-input/bal-number-input.utils.spec.ts @@ -22,6 +22,8 @@ describe('bal-number-input', () => { expect(isNumber('0.1')).toBeTruthy() expect(isNumber('42')).toBeTruthy() expect(isNumber('-42')).toBeTruthy() + expect(isNumber("40'000")).toBeTruthy() + expect(isNumber('40`000')).toBeTruthy() }) }) @@ -38,6 +40,8 @@ describe('bal-number-input', () => { expect(isNotNumber('0.1')).toBeFalsy() expect(isNotNumber('42')).toBeFalsy() expect(isNotNumber('-42')).toBeFalsy() + expect(isNumber("40'000")).toBeTruthy() + expect(isNumber('40`000')).toBeTruthy() }) }) @@ -93,6 +97,16 @@ describe('bal-number-input', () => { const result = toNumber('.') expect(result).toBeUndefined() }) + + it('should return number and ignore separator', () => { + const result = toNumber("42'000") + expect(result).toBe(42000) + }) + + it('should return number and ignore autofill separator', () => { + const result = toNumber('42`000') + expect(result).toBe(42000) + }) }) describe('toFixedNumber', () => { @@ -189,6 +203,8 @@ describe('bal-number-input', () => { expect( validateKeyDown({ key: '4', + ctrlKey: false, + metaKey: false, newValue: '4', oldValue: '', selectionStart: 0, @@ -200,6 +216,8 @@ describe('bal-number-input', () => { expect( validateKeyDown({ key: '2', + ctrlKey: false, + metaKey: false, newValue: '42', oldValue: '4', selectionStart: 1, @@ -214,6 +232,8 @@ describe('bal-number-input', () => { expect( validateKeyDown({ key: '-', + ctrlKey: false, + metaKey: false, newValue: '-', oldValue: '', selectionStart: 0, @@ -225,6 +245,8 @@ describe('bal-number-input', () => { expect( validateKeyDown({ key: '2', + ctrlKey: false, + metaKey: false, newValue: '-2', oldValue: '-', selectionStart: 1, @@ -236,6 +258,8 @@ describe('bal-number-input', () => { expect( validateKeyDown({ key: '.', + ctrlKey: false, + metaKey: false, newValue: '2.', oldValue: '2', selectionStart: 2, @@ -249,6 +273,8 @@ describe('bal-number-input', () => { expect( validateKeyDown({ key: '4', + ctrlKey: false, + metaKey: false, newValue: '4', oldValue: '', selectionStart: 0, @@ -260,6 +286,8 @@ describe('bal-number-input', () => { expect( validateKeyDown({ key: '.', + ctrlKey: false, + metaKey: false, newValue: '4.', oldValue: '4', selectionStart: 1, @@ -271,6 +299,8 @@ describe('bal-number-input', () => { expect( validateKeyDown({ key: '2', + ctrlKey: false, + metaKey: false, newValue: '4.2', oldValue: '4.', selectionStart: 2, @@ -284,6 +314,8 @@ describe('bal-number-input', () => { expect( validateKeyDown({ key: '-', + ctrlKey: false, + metaKey: false, newValue: '1-', oldValue: '1', selectionStart: 1, @@ -295,6 +327,8 @@ describe('bal-number-input', () => { expect( validateKeyDown({ key: '-', + ctrlKey: false, + metaKey: false, newValue: '--', oldValue: '-', selectionStart: 1, @@ -308,6 +342,8 @@ describe('bal-number-input', () => { expect( validateKeyDown({ key: '.', + ctrlKey: false, + metaKey: false, newValue: '.2.', oldValue: '.2', selectionStart: 3, @@ -319,6 +355,8 @@ describe('bal-number-input', () => { expect( validateKeyDown({ key: '.', + ctrlKey: false, + metaKey: false, newValue: '..', oldValue: '.', selectionStart: 2, @@ -332,6 +370,8 @@ describe('bal-number-input', () => { expect( validateKeyDown({ key: 'a', + ctrlKey: false, + metaKey: false, newValue: 'a', oldValue: '', selectionStart: 1, @@ -343,6 +383,8 @@ describe('bal-number-input', () => { expect( validateKeyDown({ key: '!', + ctrlKey: false, + metaKey: false, newValue: '!', oldValue: '', selectionStart: 1, @@ -356,6 +398,8 @@ describe('bal-number-input', () => { expect( validateKeyDown({ key: '9', + ctrlKey: false, + metaKey: false, newValue: '1.429', oldValue: '1.42', selectionStart: 5, @@ -367,6 +411,8 @@ describe('bal-number-input', () => { expect( validateKeyDown({ key: 'ArrowLeft', + ctrlKey: false, + metaKey: false, newValue: '1.42', oldValue: '1.42', selectionStart: 5, @@ -380,6 +426,8 @@ describe('bal-number-input', () => { expect( validateKeyDown({ key: 'ArrowLeft', + ctrlKey: false, + metaKey: false, newValue: '1.42', oldValue: '1.42', selectionStart: 3, @@ -391,6 +439,8 @@ describe('bal-number-input', () => { expect( validateKeyDown({ key: 'Escape', + ctrlKey: false, + metaKey: false, newValue: '1.4', oldValue: '1.42', selectionStart: 4, @@ -402,6 +452,49 @@ describe('bal-number-input', () => { expect( validateKeyDown({ key: 'Delete', + ctrlKey: false, + metaKey: false, + newValue: '1.2', + oldValue: '1.42', + selectionStart: 2, + selectionEnd: 2, + decimal: 2, + }), + ).toBeTruthy() + }) + + test('should allow select all, copy and paste', () => { + expect( + validateKeyDown({ + key: 'a', + ctrlKey: false, + metaKey: true, + newValue: '1.42', + oldValue: '1.42', + selectionStart: 3, + selectionEnd: 3, + decimal: 2, + }), + ).toBeTruthy() + + expect( + validateKeyDown({ + key: 'c', + ctrlKey: true, + metaKey: false, + newValue: '1.4', + oldValue: '1.42', + selectionStart: 4, + selectionEnd: 4, + decimal: 2, + }), + ).toBeTruthy() + + expect( + validateKeyDown({ + key: 'v', + ctrlKey: false, + metaKey: true, newValue: '1.2', oldValue: '1.42', selectionStart: 2, diff --git a/packages/core/src/components/bal-number-input/bal-number-input.utils.ts b/packages/core/src/components/bal-number-input/bal-number-input.utils.ts index 29a780e3d7..a5d64249d7 100644 --- a/packages/core/src/components/bal-number-input/bal-number-input.utils.ts +++ b/packages/core/src/components/bal-number-input/bal-number-input.utils.ts @@ -1,6 +1,6 @@ import isNil from 'lodash.isnil' import { ACTION_KEYS, NUMBER_KEYS } from '../../utils/constants/keys.constant' -import { formatLocaleNumber, getDecimalSeparator, getNegativeSymbol } from '../../utils/number' +import { formatLocaleNumber, getDecimalSeparator, getNegativeSymbol, getThousandSeparator } from '../../utils/number' import isNaN from 'lodash.isnan' export function isNumber(value: any): boolean { @@ -13,31 +13,41 @@ export function isNotNumber(value: any): boolean { } export function toNumber(value: any, decimalPoints = 0): number | undefined { + let val = value if ( - value === '' || - value === undefined || - value === null || - isNaN(value) || - value === getNegativeSymbol() || - value === getDecimalSeparator() || - !isNumber(value) + val === '' || + val === undefined || + val === null || + isNaN(val) || + val === getNegativeSymbol() || + val === getDecimalSeparator() || + !isNumber(val) ) { return undefined } - return decimalPoints === 0 ? parseInt(value, 10) : parseFloat(value) + if (typeof val === 'string') { + val = val.split(getThousandSeparator()).join('').split('`').join('').split("'").join('') + } + + return decimalPoints === 0 ? parseInt(val, 10) : parseFloat(val) } export function toFixedNumber(value: string, decimalPoints = 0): string { - if (isNil(value)) { + let val = value + if (isNil(val)) { return '' } - if (value.charAt(0) === getDecimalSeparator()) { - value = `0${value}` + if (typeof val === 'string') { + val = val.split(getThousandSeparator()).join('').split('`').join('').split("'").join('') } - const num = decimalPoints === 0 ? parseInt(value, 10) : parseFloat(value.replace(getDecimalSeparator(), '.')) + if (val.charAt(0) === getDecimalSeparator()) { + val = `0${val}` + } + + const num = decimalPoints === 0 ? parseInt(val, 10) : parseFloat(val.replace(getDecimalSeparator(), '.')) return isNaN(num) ? '' : num.toFixed(decimalPoints) } @@ -62,6 +72,8 @@ export function toUserFormattedNumber(value: string, decimalPoints = 0, suffix = export type ValidateKeyDownOptions = { decimal: number key: string + ctrlKey: boolean + metaKey: boolean newValue: string oldValue: string selectionStart: number | null @@ -72,11 +84,19 @@ export const countDecimalSeparators = (value: string) => (value.split(getDecimal export function validateKeyDown({ key, + ctrlKey, + metaKey, selectionStart, selectionEnd, newValue, decimal, }: ValidateKeyDownOptions): boolean { + // + // allow select all, copy and paste + if (['a', 'c', 'v'].includes(key) && (ctrlKey || metaKey)) { + return true + } + // // only allow negative symbols at the start of the input if (key === getNegativeSymbol() && selectionStart && selectionStart > 0 && selectionEnd && selectionEnd > 0) { diff --git a/packages/core/src/components/bal-number-input/test/bal-number-input.cy.html b/packages/core/src/components/bal-number-input/test/bal-number-input.cy.html index f6c6b31d31..af6eb6a72e 100644 --- a/packages/core/src/components/bal-number-input/test/bal-number-input.cy.html +++ b/packages/core/src/components/bal-number-input/test/bal-number-input.cy.html @@ -12,12 +12,13 @@
Basic -
+
+ Form Reset -
+
@@ -34,6 +35,51 @@
+ + Form Reset +
+ + + +
+ + Autocomplete +
+
+ + + + First Name + + + + + + + + Last Name + + + + + + + + Salary + + + + + + + + +
+