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

useNumberField mutates number so screenreader users won’t know that their value has changed #4967

Open
dgreene1 opened this issue Aug 23, 2023 · 5 comments · May be fixed by #6520
Open

useNumberField mutates number so screenreader users won’t know that their value has changed #4967

dgreene1 opened this issue Aug 23, 2023 · 5 comments · May be fixed by #6520

Comments

@dgreene1
Copy link

dgreene1 commented Aug 23, 2023

Provide a general summary of the issue here

Assume that on one page a user is allowed to type 3.000.000,25 for a German formatted number and then on another one they are only allowed to provide a US number (3,000,000.25).

The “steps to reproduce” below shows that, due to useNumberField mutating/formatting the result, the final value is nowhere near what the user tried to type. And while a visual user can see that what they typed is not what came up, a screenreader user will have no idea that their desired number (three million and one quarter) became something very far off (three).

🤔 Expected Behavior?

The widget would inform the user that an error has occurred due to the user assuming a locale that was not what is set.

😯 Current Behavior

The widget mutates the value

💁 Possible Solution

I’m honestly not sure. For me, it’s reason enough to not use a masking solution and to utilize error validation only as I share in https://dev.to/dgreene1/why-masking-is-not-accessible-i3c

But if we were to fix it in react-aria, we’d somehow have to be able to identify that the user has attempted to type a number that would be accurate in a locale that is not the one currently set in useLocale. I honestly don’t know if this would be even possible. But if someone can think of how we can catch the full input and operate off of that assumption, that would allow us to be predictive.

🔦 Context

The scenario where this is most relevant is where a German user copies a value out of an Excel file or a PDF that has USA format.

For my particular usage context, we had considered iteratively migrating to useNumberField. That means that until we had completely migrated every page over to useNumberField, our standard inputs would have differing behavior.

Some pages are internationalized and some are not yet supporting internationalization.

A requirement for us is that we are very clear to users when a page is or is not supporting various formats.

🖥️ Steps to Reproduce

  1. Navigate to https://react-spectrum.adobe.com/react-aria/useNumberField.html#decimals
  2. type 3.000.000,25 (which is three million and one quarter in german formatting)
  3. the widget will convert the value to 3

Alternatively, set your locale to de-DE and do the following:

  1. Navigate to https://react-spectrum.adobe.com/react-aria/useNumberField.html#decimals
  2. type 3,000,000.25 (which is three million and one quarter in USA formatting)
  3. the widget will convert the value to 3

Version

latest

What browsers are you seeing the problem on?

Chrome

If other, please specify.

No response

What operating system are you using?

Windows 10

🧢 Your Company/Team

No response

🕷 Tracking Issue

No response

@devongovett
Copy link
Member

I don't think this has to do with masking. At some point we have to parse the number in order to determine what the value is. The reformatting that you observe is the result of that parsing. This is at some level always going to be a guess about what the user meant. It is informed by their locale, assuming that they used the format common for their language.

Some pages are internationalized and some are not yet supporting internationalization.

Maybe you could override the locale for a number field using I18nProvider so it is hard coded to "en-US" or any other locale if you want it to be consistent everywhere while you migrate?

@dgreene1
Copy link
Author

dgreene1 commented Aug 23, 2023

@devongovett thank you for your quick response. Before you responded, I update the post above to explain the scenario where a German user copies 3,000,000.25 out of an Excel file and pasted into our UI (which is hardcoded to de-DE).

When the parsing occurs, I think the react-aria library could identify that 3,000,000.25 is a valid number, just not for the current locale.

That would be a way to solve the parsing issue that you describe.

In the absence of that, the parsing is lying to the user, which is particularly insidious for screenreader users who aren’t getting any kind of feedback that reformatting has occurred.

Should I reword the description to clarify that it’s a parsing/formatting issue instead of a masking issue?

@snowystinger
Copy link
Member

In order to determine whether a number is parseable, we use the current locale and numbering system and formatting options.

If we just wanted to check if it was A valid number, that's 34 locales, 3 numbering systems, and a ton of formatting options. I'm not sure how we'd decide that one of them is more valid a parsed number than another.

For example, if we use your 3,000,000.25, with the given combination in the example you linked, 3 is a valid number we could parse that input to, as seen by following your steps and the result, which is 3.

We could possibly improve by checking the order in which the groups and decimals separators appear. I do not know of any number having a group separator past the decimals place. However, my knowledge is far from complete on that.

However, I think that is only addressing a single specific symptom. Parsing numbers, especially formatted ones, is hard. I think it would be more helpful across all combinations to approach this in two ways. One, clearly communicate what is expected in terms of format. That'll be more up to applications. And two, we should address the issue for screen readers so they know what their number was parsed to.

@dgreene1
Copy link
Author

dgreene1 commented Sep 4, 2023

Yes , this would go a long way to solving this:

we should address the issue for screen readers so they know what their number was parsed to.

but how can we do that?

@snowystinger
Copy link
Member

That's a great question. Unfortunately, when we blur away, and the value gets formatted, that's a bit late. We'd be announcing the next element we have focus on. We could get ahead of that with an assertive live announcer, but that's generally a last resort.
Eventually, we'll support formatting on 'Enter', but we haven't done that yet. That may also be too late because 'Enter' in a form field can submit the form.

We also can't be changing/formatting the number while the user is typing.

What we might be able to do, though, is if a paste operation replaces the entire contents of the field, we could format it right then and there. This wouldn't change the cursor position at the end of the operation.

majornista added a commit that referenced this issue Jun 10, 2024
… know that their value has changed

Improvements to behavior when pasting a value from a different locale into a NumberField.

A pasted string containing groups and a decimal, like "3 000 000.25", "3,000,000.25" or "3.000.000,25", will now attempt to parse using supported locales rather than only parsing using the current locale.

Where there is ambiguity, as with "1,000" or "1.000", the pasted value will be parsed using the current locale. For example, "1.000" will be interpreted as "1" in en-US, and "1,000" will be "1" in de-DE.

If the pasted text is different from the resulting input text, the screen reader should announce "Pasted value: {value}" politely, so that the user hears the new value.
majornista added a commit that referenced this issue Jun 19, 2024
… know that their value has changed

Improvements to behavior when pasting a value from a different locale into a NumberField.

A pasted string containing groups and a decimal, like "3 000 000.25", "3,000,000.25" or "3.000.000,25", will now attempt to parse using supported locales rather than only parsing using the current locale.

Where there is ambiguity, as with "1,000" or "1.000", the pasted value will be parsed using the current locale. For example, "1.000" will be interpreted as "1" in en-US, and "1,000" will be "1" in de-DE.

If the pasted text is different from the resulting input text, the screen reader should announce "Pasted value: {value}" politely, so that the user hears the new value.
majornista added a commit that referenced this issue Jun 24, 2024
… know that their value has changed

Improvements to behavior when pasting a value from a different locale into a NumberField.

A pasted string containing groups and a decimal, like "3 000 000.25", "3,000,000.25" or "3.000.000,25", will now attempt to parse using supported locales rather than only parsing using the current locale.

Where there is ambiguity, as with "1,000" or "1.000", the pasted value will be parsed using the current locale. For example, "1.000" will be interpreted as "1" in en-US, and "1,000" will be "1" in de-DE.

If the pasted text is different from the resulting input text, the screen reader should announce "Pasted value: {value}" politely, so that the user hears the new value.
majornista added a commit that referenced this issue Jun 24, 2024
… know that their value has changed

Improvements to behavior when pasting a value from a different locale into a NumberField.

A pasted string containing groups and a decimal, like "3 000 000.25", "3,000,000.25" or "3.000.000,25", will now attempt to parse using supported locales rather than only parsing using the current locale.

Where there is ambiguity, as with "1,000" or "1.000", the pasted value will be parsed using the current locale. For example, "1.000" will be interpreted as "1" in en-US, and "1,000" will be "1" in de-DE.

If the pasted text is different from the resulting input text, the screen reader should announce "Pasted value: {value}" politely, so that the user hears the new value.
majornista added a commit that referenced this issue Jun 26, 2024
… know that their value has changed

Improvements to behavior when pasting a value from a different locale into a NumberField.

A pasted string containing groups and a decimal, like "3 000 000.25", "3,000,000.25" or "3.000.000,25", will now attempt to parse using supported locales rather than only parsing using the current locale.

Where there is ambiguity, as with "1,000" or "1.000", the pasted value will be parsed using the current locale. For example, "1.000" will be interpreted as "1" in en-US, and "1,000" will be "1" in de-DE.

If the pasted text is different from the resulting input text, the screen reader should announce "Pasted value: {value}" politely, so that the user hears the new value.
majornista added a commit that referenced this issue Jul 11, 2024
… know that their value has changed

Improvements to behavior when pasting a value from a different locale into a NumberField.

A pasted string containing groups and a decimal, like "3 000 000.25", "3,000,000.25" or "3.000.000,25", will now attempt to parse using supported locales rather than only parsing using the current locale.

Where there is ambiguity, as with "1,000" or "1.000", the pasted value will be parsed using the current locale. For example, "1.000" will be interpreted as "1" in en-US, and "1,000" will be "1" in de-DE.

If the pasted text is different from the resulting input text, the screen reader should announce "Pasted value: {value}" politely, so that the user hears the new value.
majornista added a commit that referenced this issue Jul 15, 2024
… know that their value has changed

Improvements to behavior when pasting a value from a different locale into a NumberField.

A pasted string containing groups and a decimal, like "3 000 000.25", "3,000,000.25" or "3.000.000,25", will now attempt to parse using supported locales rather than only parsing using the current locale.

Where there is ambiguity, as with "1,000" or "1.000", the pasted value will be parsed using the current locale. For example, "1.000" will be interpreted as "1" in en-US, and "1,000" will be "1" in de-DE.

If the pasted text is different from the resulting input text, the screen reader should announce "Pasted value: {value}" politely, so that the user hears the new value.
majornista added a commit that referenced this issue Jul 15, 2024
… know that their value has changed

Improvements to behavior when pasting a value from a different locale into a NumberField.

A pasted string containing groups and a decimal, like "3 000 000.25", "3,000,000.25" or "3.000.000,25", will now attempt to parse using supported locales rather than only parsing using the current locale.

Where there is ambiguity, as with "1,000" or "1.000", the pasted value will be parsed using the current locale. For example, "1.000" will be interpreted as "1" in en-US, and "1,000" will be "1" in de-DE.

If the pasted text is different from the resulting input text, the screen reader should announce "Pasted value: {value}" politely, so that the user hears the new value.
majornista added a commit that referenced this issue Jul 15, 2024
… know that their value has changed

Improvements to behavior when pasting a value from a different locale into a NumberField.

A pasted string containing groups and a decimal, like "3 000 000.25", "3,000,000.25" or "3.000.000,25", will now attempt to parse using supported locales rather than only parsing using the current locale.

Where there is ambiguity, as with "1,000" or "1.000", the pasted value will be parsed using the current locale. For example, "1.000" will be interpreted as "1" in en-US, and "1,000" will be "1" in de-DE.

If the pasted text is different from the resulting input text, the screen reader should announce "Pasted value: {value}" politely, so that the user hears the new value.
majornista added a commit that referenced this issue Jul 23, 2024
… know that their value has changed

Improvements to behavior when pasting a value from a different locale into a NumberField.

A pasted string containing groups and a decimal, like "3 000 000.25", "3,000,000.25" or "3.000.000,25", will now attempt to parse using supported locales rather than only parsing using the current locale.

Where there is ambiguity, as with "1,000" or "1.000", the pasted value will be parsed using the current locale. For example, "1.000" will be interpreted as "1" in en-US, and "1,000" will be "1" in de-DE.

If the pasted text is different from the resulting input text, the screen reader should announce "Pasted value: {value}" politely, so that the user hears the new value.
majornista added a commit that referenced this issue Jul 23, 2024
…mbering systems

1. If the value fails to parse using default parser and numbering system for the locale, try other locales and numbering systems.
2. Use a RegEx to capture the decimal part of the value.
3. Improve logic for replacing decimal symbol in the value with the decimal symbol for the current locale.

Ambiguous values may still fail to round trip. For example 123,456, will be parsed as 123.456 in locales that use a comma as the decimal symbol, but 123456 in those that use a period as the decimal symbol.

"Round trips should fully reverse NumberFormat" test will console.log the details of any of these failures.

3. Strip leading zeros from the integer part using a RegEx rather than parseInt.
4. Handle accounting formatting when handling literals after the last numeral in the value.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants