Repository: h2oai/wave
File: ui/src/spinbox.tsx (TypeScript)

Code before:
          // We can't use parseValue because it requires casting to number which will remove the trailling zeros.
          const [head, tail = ''] = val.split('.')
          setVal(`${head}.${tail.slice(0, precision)}`)
        }
        else setVal(String(newValue))
        return newValue
      }, [max, min, parseValue, precision]),
      handleOnInput = React.useCallback((val: U) => {
        wave.args[name] = val
        m.value = val
        if (trigger) wave.push()
        // eslint-disable-next-line react-hooks/exhaustive-deps
      }, [name, trigger]),
      debouncedHandleOnInput = React.useRef(wave.debounce(DEBOUNCE_TIMEOUT, handleOnInput)),
      onInput = (e: React.SyntheticEvent<HTMLElement>) => {
        const numVal = handleValue((e.target as HTMLInputElement).value)
        trigger ? debouncedHandleOnInput.current(numVal) : handleOnInput(numVal)
      }

    React.useEffect(() => {
      if (value === wave.args[name]) return
      const val = (value < min) ? min : ((value > max) ? max : value)
      wave.args[name] = val
      setVal(String(val))
      // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [value])

    return (
      <Fluent.SpinButton
        inputProps={{ 'data-test': name, value: val } as React.InputHTMLAttributes<HTMLInputElement>}
        label={label}
        onInput={onInput}
        min={min}
        max={max}
        step={step}
        onIncrement={onIncrement}
        onDecrement={onDecrement}
        disabled={disabled}
      />
    )
  }

Review comment (suggestion):
We probably want to include all the necessary dependencies than just `value`, right? This way the `value` would be always in bounds of minmax.

If so, please add a quick unit test validating the value is changed properly on minmax update. Should be pretty fast.

Code after:
          // We can't use parseValue because it requires casting to number which will remove the trailling zeros.
          const [head, tail = ''] = val.split('.')
          setVal(`${head}.${tail.slice(0, precision)}`)
        }
        else setVal(String(newValue))
        return newValue
      }, [max, min, parseValue, precision]),
      handleOnInput = React.useCallback((val: U) => {
        wave.args[name] = val
        if (trigger) wave.push()
      }, [name, trigger]),
      debouncedHandleOnInput = React.useRef(wave.debounce(DEBOUNCE_TIMEOUT, handleOnInput)),
      onInput = (e: React.SyntheticEvent<HTMLElement>) => {
        const inputValue = (e.target as HTMLInputElement).value
        const numVal = handleValue(inputValue)
        if (String(numVal) === inputValue) m.value = numVal
        trigger ? debouncedHandleOnInput.current(numVal) : handleOnInput(numVal)
      }

    React.useEffect(() => {
      const val = (value < min) ? min : ((value > max) ? max : value)
      wave.args[name] = val
      setVal(String(val))
    }, [max, min, name, value])

    return (
      <Fluent.SpinButton
        inputProps={{ 'data-test': name, value: val } as React.InputHTMLAttributes<HTMLInputElement>}
        label={label}
        onInput={onInput}
        min={min}
        max={max}
        step={step}
        onIncrement={onIncrement}
        onDecrement={onDecrement}
        disabled={disabled}
      />
    )
  }