Skip to content

feat: NumberParser assorted bug fixes#8592

Open
snowystinger wants to merge 25 commits intomainfrom
fix-some-number-validation
Open

feat: NumberParser assorted bug fixes#8592
snowystinger wants to merge 25 commits intomainfrom
fix-some-number-validation

Conversation

@snowystinger
Copy link
Member

@snowystinger snowystinger commented Jul 21, 2025

Closes #7015, Closes #3862, Closes #6861, Closes #5927, Closes #9086

Finally had some thoughts on how to address some of the issues that were being tackled in #6520
But due to concerns, it has been removed #8592 (comment)
Will open a follow up PR that has these changes for further consideration.

✅ Pull Request Checklist:

  • Included link to corresponding React Spectrum GitHub Issue.
  • Added/updated unit tests and storybook for this change (for new code or code which already has tests).
  • Filled out test instructions.
  • Updated documentation (if it already exists for this component).
  • Looked at the Accessibility Practices for this feature - Aria Practices

📝 Test Instructions:

See each bug for reproductions.

🧢 Your Project:

@rspbot
Copy link

rspbot commented Jul 21, 2025

@rspbot
Copy link

rspbot commented Jul 21, 2025

# Conflicts:
#	packages/@internationalized/number/src/NumberParser.ts
#	packages/@internationalized/number/test/NumberParser.test.js
@rspbot
Copy link

rspbot commented Aug 22, 2025

@rspbot
Copy link

rspbot commented Aug 27, 2025

@rspbot
Copy link

rspbot commented Sep 11, 2025

LFDanLu
LFDanLu previously approved these changes Sep 17, 2025
Copy link
Member

@LFDanLu LFDanLu left a comment

Choose a reason for hiding this comment

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

Verified that this fixes the linked issues and the updates to the parser logic make sense. Perhaps a bit silly of a case, but what would we expect if someone types "123, 456"? It has the group separator and a non matching symbol from a different format and thus resolves to "123.456" since the space becomes the decimal separator but perhaps someone could expect it to resolve to "123,456"

Copy link
Member

Choose a reason for hiding this comment

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

verified the fix for #6861 here, but noticed that I can't go into the negatives it seems? It seems to cycle between what I think is 1 and 0.

Copy link
Member Author

Choose a reason for hiding this comment

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

interesting case. I'll look into that as follow up

@snowystinger
Copy link
Member Author

Verified that this fixes the linked issues and the updates to the parser logic make sense. Perhaps a bit silly of a case, but what would we expect if someone types "123, 456"? It has the group separator and a non matching symbol from a different format and thus resolves to "123.456" since the space becomes the decimal separator but perhaps someone could expect it to resolve to "123,456"

Good question, that is a little ambiguous, think I should fail to parse it instead? I could limit the failure to parse case to no numbers between the two symbols.

@LFDanLu
Copy link
Member

LFDanLu commented Sep 18, 2025

Yeah I think maybe err on the side of caution and fail to parse it if there aren't any numbers between the two symbols

@rspbot
Copy link

rspbot commented Oct 26, 2025

@rspbot
Copy link

rspbot commented Oct 26, 2025

@rspbot
Copy link

rspbot commented Oct 28, 2025

@rspbot
Copy link

rspbot commented Jan 5, 2026

@rspbot
Copy link

rspbot commented Jan 7, 2026

# Conflicts:
#	packages/react-aria-components/test/NumberField.test.js
@rspbot
Copy link

rspbot commented Jan 28, 2026

LFDanLu
LFDanLu previously approved these changes Jan 30, 2026
Copy link
Member

@LFDanLu LFDanLu left a comment

Choose a reason for hiding this comment

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

LGTM, reverified that the issues linked are all fixed now and tried a variety of separator/decimal combinations. Might be worthwhile creating a ticket for the followup items just so we don't forget

value = replaceAll(value, '__DECIMAL__', this.symbols.decimal!);
}
}

Copy link
Member

Choose a reason for hiding this comment

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

what is this doing? this makes me scared. 😂

Copy link
Member Author

Choose a reason for hiding this comment

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

These are just temp strings that allow you to swap group and decimal characters, for instance
"1.200,001"
If I try to replace either one immediately, then the number is changed in a way I can't recover
"1.200.001" or "1,200,001"

So I just replace them with the substitution
"1__GROUP__200__DECIMAL__001" so that I can easily fix it to "1,200.001"

Could I do it with one less set of replaces, yes. I am sometimes a little extra explicit for my own reading comprehension. I'll update it.

@devongovett
Copy link
Member

I feel like we're making a lot of assumptions here. How often are people pasting a value that's formatted in a different locale? Looking through the issues above, several of them seem more related to parsing in the wrong locale (e.g. detecting , as arabic), or other issues. Maybe it would help me if you could point out which parts of the changes relate to which issues?

@rspbot
Copy link

rspbot commented Feb 1, 2026

@snowystinger
Copy link
Member Author

snowystinger commented Feb 2, 2026

Maybe it would help me if you could point out which parts of the changes relate to which issues?

Sure, I'll do my best. Please note that many of the changes came from running the fuzzing tests compliments of FastCheck.

#6861
Which is tested by, NumberParser.test line 207
It's implemented on line 205 of NumberParser and has a comment.

#5927 was fixed by line 244
It's test is here https://github.com/adobe/react-spectrum/pull/8592/changes#diff-65d8f601ed61f6cdfe627db4fbb5568cdb164084d0a8c637cc9d3e3ee58a6c77R326

#3862 and #7015 were fixed line 237 in tandem with the fix for 5927
It's test is here https://github.com/adobe/react-spectrum/pull/8592/changes#diff-7bf83599612e94f27f7a4a9ce5457ccc8c68b7ea557c05b16b02e2ea6515e426R417

#9086
Test is covered in NumberParser.test.js, lines 64-67, 427-430
The change was to check the resolvedOptions for useGrouping, line 135 of NumberParser, and return NaN if there is a group symbol but useGrouping is false, line 140.
This, and the logic for pulling the number out of the string in sanitize, line 209 because they can be valid partial numbers and there can be literals

There's more info about my thinking here: #9089

Note, the check for isGroupSymbolAllowed became a combined check with this.symbols.group.

Finally, the other changes in this PR came from #6520 and 9089 where I determined we're making assumptions and things are ambiguous currently. So this PR actually intends to make less assumptions. There are code comments on the other three blocks of code to explain.
Multiple decimal separators
Decimal separator before group
Different formats

And then the last block is in support of those three https://github.com/adobe/react-spectrum/pull/8592/changes#diff-983e1f4675c7ada3abd53503639a16a3f999170790ad28dc5b3f9b0ad6ec8731R209
Which is tested here: https://github.com/adobe/react-spectrum/pull/8592/changes#diff-65d8f601ed61f6cdfe627db4fbb5568cdb164084d0a8c637cc9d3e3ee58a6c77R215
From my testing, numbers may contains things we previously considered a global literal, but when it's inside the number may have meaning. One case was pasting from another format (someone copying from a non-locale aware context (PDF) into our app), but there was also the number as a literal case. So I've opted to preserve the number itself and reduce our global deletions in the string.

If it would make things better, I can split out some of these into their own PRs.

@rspbot
Copy link

rspbot commented Feb 4, 2026

@devongovett
Copy link
Member

Yeah I'm still a bit concerned about trying to guess what people meant and auto-correct it, e.g. the swapping of group separators and decimals. Someone might be in the middle of editing, blur to go check another tab, come back and what they were doing has re-formatted to a completely different value. Disambiguating this characters is one of the main reasons we use the locale for parsing in the first place.


// Replace literals in the parts outside the number
beforeNumber = beforeNumber.replace(this.symbols.literals, '');
afterNumber = afterNumber.replace(this.symbols.literals, '');
Copy link
Member

Choose a reason for hiding this comment

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

When are literals allowed inside the number?

Copy link
Member Author

@snowystinger snowystinger Feb 8, 2026

Choose a reason for hiding this comment

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

group separators and decimals may be literals in other locales, very specifically, whitespaces

# Conflicts:
#	packages/react-aria-components/test/NumberField.test.js
@snowystinger
Copy link
Member Author

Someone might be in the middle of editing, blur to go check another tab, come back and what they were doing has re-formatted to a completely different value

This is already the case, the value could be cleared, reverted to a previous valid value, or formatted as a complete value.

@rspbot
Copy link

rspbot commented Feb 9, 2026

@snowystinger snowystinger changed the title feat: fuzzier number matching for NumberParser feat: NumberParser assorted bug fixes Feb 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

4 participants