Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DO NO MERGE]: fix(number-field): separator interpreted as decimal #4552

Closed
42 changes: 35 additions & 7 deletions packages/number-field/src/NumberField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,18 +210,41 @@ export class NumberField extends TextfieldBase {
);
}

private decimalsChars = new Set(['.', ',']);
private valueBeforeFocus: string = '';
private isIntentDecimal: boolean = false;

private convertValueToNumber(value: string): number {
const separators = this.valueBeforeFocus
.split('')
.filter((char) => this.decimalsChars.has(char));
const uniqueSeparators = new Set(separators);

if (isIPhone() && this.inputElement.inputMode === 'decimal') {
const parts = this.numberFormatter.formatToParts(1000.1);
const sourceDecimal = value
.split('')
.find((char) => char === ',' || char === '.');
const replacementDecimal = parts.find(
(part) => part.type === 'decimal'
)?.value;
if (sourceDecimal && replacementDecimal) {
value = value.replace(sourceDecimal, replacementDecimal);
)!.value;

for (const separator of uniqueSeparators) {
const isDecimalSeparator = separator === replacementDecimal;
if (!isDecimalSeparator && !this.isIntentDecimal) {
value = value.replace(new RegExp(separator, 'g'), '');
}
}

let hasReplacedDecimal = false;
const valueChars = value.split('');
for (let index = valueChars.length - 1; index >= 0; index--) {
const char = valueChars[index];
if (this.decimalsChars.has(char)) {
if (!hasReplacedDecimal) {
valueChars[index] = replacementDecimal;
hasReplacedDecimal = true;
} else valueChars[index] = '';
}
}
value = valueChars.join('');
}
return this.numberParser.parse(value);
}
Expand Down Expand Up @@ -379,12 +402,14 @@ export class NumberField extends TextfieldBase {
this._trackingValue = this.inputValue;
this.keyboardFocused = !this.readonly && true;
this.addEventListener('wheel', this.onScroll, { passive: false });
this.valueBeforeFocus = this.inputElement.value;
}

protected override onBlur(_event: FocusEvent): void {
super.onBlur(_event);
this.keyboardFocused = !this.readonly && false;
this.removeEventListener('wheel', this.onScroll);
this.isIntentDecimal = false;
}

private handleFocusin(): void {
Expand Down Expand Up @@ -441,7 +466,7 @@ export class NumberField extends TextfieldBase {
});
}

protected override handleInput(event: Event): void {
protected override handleInput(event: InputEvent): void {
if (this.isComposing) {
event.stopPropagation();
return;
Expand All @@ -454,6 +479,9 @@ export class NumberField extends TextfieldBase {
''
);
}
if (event.data && this.decimalsChars.has(event.data))
this.isIntentDecimal = true;

const { value: originalValue, selectionStart } = this.inputElement;
const value = originalValue
.split('')
Expand Down
34 changes: 34 additions & 0 deletions packages/number-field/test/number-field.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,40 @@ describe('NumberField', () => {
expect(el.focusElement.value).to.equal('13 377 331');
});
});
xit('correctly interprets decimal point on iPhone', async () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created a test with some common usecases but haven't been able to run it since I couldn't find a way to mock isIphone.

// setUserAgent is not currently supported by Playwright
await setUserAgent(
'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1'
);
const el = await getElFrom(decimals({ value: 1234 }));
expect(el.formattedValue).to.equal('1,234');

el.focus();
await sendKeys({ press: 'Backspace' });
el.blur();
expect(el.formattedValue).to.equal('123');

el.focus();
await sendKeys({ type: '45' });
el.blur();
expect(el.formattedValue).to.equal('12,345');

el.focus();
await sendKeys({ type: ',6' });
el.blur();
expect(el.formattedValue).to.equal('12,345.6');

el.focus();
await sendKeys({ type: ',7' });
el.blur();
expect(el.formattedValue).to.equal('123,456.7');

el.focus();
await sendKeys({ press: 'Backspace' });
await sendKeys({ press: 'Backspace' });
el.blur();
expect(el.formattedValue).to.equal('123,456');
});
describe('Step', () => {
it('can be 0', async () => {
const el = await getElFrom(
Expand Down
Loading