adobe / react-spectrum

A collection of libraries and tools that help you build adaptive, accessible, and robust user experiences.
https://react-spectrum.adobe.com
Apache License 2.0
13.03k stars 1.13k forks source link

@react-stately/utils::snapNumberToStep changes value if step and min have different levels of precision #6359

Open lxfschr opened 6 months ago

lxfschr commented 6 months ago

Provide a general summary of the issue here

The utility function used in useNumberFieldState snapNumberToStep, alters the input value unexpectedly and incorrectly when given different levels of precision of min and step. For instance, if I want the minimum value to be 0.001 but I want the stepper to step in increments of 1, using useNumberFieldState will return my inputValue with an extra 0.001 added onto it. It is relatively common to have a specific min and a separate desired step size, and this is causing the numbers displayed to the user to be inaccurate, and ugly, adding on extra 0s to the end.

๐Ÿค” Expected Behavior?

snapNumberToStep(1000, 0.001, undefined, 1) = 1000

๐Ÿ˜ฏ Current Behavior

snapNumberToStep(1000, 0.001, undefined, 1) = 1000.001

๐Ÿ’ Possible Solution

Ignore the min when calculating remainder. Instead of:

const remainder = (value - (isNaN(min) ? 0 : min)) % step;

Do:

const remainder = value % step;

Later on in the function, the new value is checked to see if it is below the min, so this additional check is only causing problems.

๐Ÿ”ฆ Context

I want to be able to limit the size of shapes to a small number, such as 0.00001, but also allow the user to step the number field by a reasonable step size such as 1 unit. When using the number input, it alters the value of the number I give to it.

๐Ÿ–ฅ๏ธ Steps to Reproduce

https://codesandbox.io/p/sandbox/affectionate-worker-jv6567

Version

^3.26.0

What browsers are you seeing the problem on?

Firefox, Chrome, Safari, Microsoft Edge

If other, please specify.

No response

What operating system are you using?

MacOS 14.4.1

๐Ÿงข Your Company/Team

No response

๐Ÿ•ท Tracking Issue

No response

snowystinger commented 6 months ago

Thanks for the issue, this is working as expected. https://react-spectrum.adobe.com/react-spectrum/NumberField.html#step-values https://html.spec.whatwg.org/multipage/input.html#the-step-attribute https://jsfiddle.net/aur2qmx9/2/

I suggest using min = 1 if you do not want people to use 0. As is, with step, you cannot enter a number less than 1 in your scenario. Try entering a decimal value into these examples, they will all be snapped to the step. https://react-spectrum.adobe.com/react-spectrum/NumberField.html#step-values

lxfschr commented 6 months ago

Thanks for your prompt response @snowystinger. I can see how this implementation applies to the spec for step. However, in my use case, which is a design tool, precision is important to our users. They need to be able to enter in exact values and see those exact values applied. In addition, it would be useful to be able to step through values at a reasonable increment when using steppers. For example, imagine a designer designing in centimeters. A reasonable step for centimeters might be 1 centimeter, when using the keyboard to scroll wheel to scrub on number fields. However, the minimum allowed value shouldn't be a centimeter, as even when working in centimeters you might have something that is half a centimeter or even a meter. As in, do you have any way to allow this behavior when using spectrum components?

snowystinger commented 6 months ago

I see what you're saying. A detached step value that does no "snapping". We don't currently support that.

I'm not sure there is a good way to do it without also allowing a user to supply the origin (ordinarily the min) as well. We'd also have to make whatever options be mutually exclusive. Specifying both a step and a detached step would be a conflict.

I'll bring it to the team and see if it's something we want to support.

snowystinger commented 6 months ago

The general feeling is that this would be ok, contingent on the API. If you'd like to contribute a PR or an RFC https://react-spectrum.adobe.com/contribute.html#feature-requests then that'd move the discussion along.