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

[base-ui][useNumberInput] Integrate useNumberInput with useControllableReducer #40206

Merged
merged 32 commits into from
Feb 5, 2024

Conversation

mj12albert
Copy link
Member

@mj12albert mj12albert commented Dec 14, 2023

Continues #38723
Closes #38514

Major changes:

  • When the input element is blurred, onChange is only called if the value has changed. Previously it was always called on blur even if the value remained the same.

  • The value can be null when the component has no value. The (custom) onChange will be called with null instead of undefined as the second argument when there is no value (e.g. when the <input> has been cleared). For the values of value and defaultValue, undefined is only used for determining whether the component is controlled or uncontrolled.

  • The value is no longer changed by the inputChange action (typing in the <input>), it will only change when the <input> is blurred,

  • Actions contain the underlying event (it was left out in [base-ui][NumberInput] Implement numberInputReducer #38723), they are irrelevant to the reducer but need to be passed through to the state change callback

  • I have followed (at least) the PR section of the contributing guide.

@mj12albert mj12albert added package: base-ui Specific to @mui/base component: number field This is the name of the generic UI component, not the React module! labels Dec 14, 2023
@mui-bot
Copy link

mui-bot commented Dec 14, 2023

Netlify deploy preview

@material-ui/unstyled: parsed: +1.28% , gzip: +0.93%

Bundle size report

Details of bundle changes (Toolpad)
Details of bundle changes

Generated by 🚫 dangerJS against 38ae823

@mj12albert mj12albert force-pushed the base/number-input-reducer branch 8 times, most recently from 05c7b4e to c6bc84c Compare December 15, 2023 13:42
@mj12albert mj12albert marked this pull request as ready for review December 15, 2023 15:57
@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Dec 21, 2023
@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged label Dec 27, 2023
Copy link
Member

@michaldudak michaldudak left a comment

Choose a reason for hiding this comment

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

It seems to work well. One minor thing I noticed when I played with the demos (it's also present on master) - when there was no value in the input and I pressed the increment button, I subconsciously expected 1 instead of 0 to appear. This is how the native control works (see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number)

docs/data/base/components/number-input/number-input.md Outdated Show resolved Hide resolved
@@ -1,6 +1,6 @@
{
"props": {
"defaultValue": { "type": { "name": "any" } },
"defaultValue": { "type": { "name": "number" } },
Copy link
Member

Choose a reason for hiding this comment

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

👍

@mj12albert
Copy link
Member Author

This is how the native control works (see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number)

Good catch ~ I've updated it to match the native behavior in 57b8259

Comment on lines +161 to +167
React.useEffect(() => {
if (isControlled && isNumber(value)) {
dispatch({
type: NumberInputActionTypes.resetInputValue,
});
}
}, [value, dispatch, isControlled]);
Copy link
Member Author

@mj12albert mj12albert Dec 29, 2023

Choose a reason for hiding this comment

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

@sai6855 I've adapted your fix (and the test ❤️ ) in #40348 here, as this PR will replace the manual setStates

I think it shouldn't be necessary to check for potential strings because it's not allowed for the value, and TS/PropTypes can take care of that. Also when it's uncontrolled, the inputValue should not ever get out of sync with the value like in https://github.com/mui/material-ui/issues/40340

For this, I've added an additional resetInputValue action CC @michaldudak

it('sets value to max when the input has no value and ArrowDown is pressed', async () => {
const handleChange = spy();
describe('when the input has no value and ArrowDown is pressed', () => {
it('sets value to max when max is provided', async () => {
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't match the native behavior either - the min or -1 value should be used here (see the first demo in https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number)

it('sets value to min when the input has no value and ArrowUp is pressed', async () => {
const handleChange = spy();
describe('when the input has no value and ArrowUp is pressed', () => {
it('sets value to min if min is provided', async () => {
Copy link
Member

Choose a reason for hiding this comment

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

This isn't quite how the native control works - it kinda assumes that no value = 0 (so if min = 0, pressing the ArrowUp sets the control to 1.

Copy link
Member Author

@mj12albert mj12albert Jan 2, 2024

Choose a reason for hiding this comment

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

@michaldudak This was intentional ~ I think it's a worthwhile improvement from the native behavior 😬

If a range is defined with min and/or max in a cleared NumberInput, it feels like a better UX to start incrementing from min on arrow up, and start decrementing from the max on arrow down

If arrow down starts on the min value, a 2nd arrow down will do nothing, and I feel its more natural to tap the same arrow 2-3 times – e.g. increment from min to min+3, or decrement from max to max-3 – rather than "down-up-up-up"

And luckily since the native number input isn't used in the wild that much, it shouldn't be something a typical web user would need to "unlearn", what do you think?

PS: Did a quick check as well, react-aria(react-spectrum) also implemented this behavior – sandbox - but mantine and Chakra do not

Copy link
Member Author

Choose a reason for hiding this comment

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

BTW I would love to get @colmtuite's opinion on this as well ~

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes I agree @mj12albert. When min or max is set, it should increment/decrement from those values imo.

Copy link
Member

Choose a reason for hiding this comment

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

All right, then. Let's get it merged!

@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Jan 17, 2024
@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged label Jan 18, 2024
@mj12albert mj12albert merged commit 59addf0 into mui:master Feb 5, 2024
22 checks passed
@mj12albert mj12albert deleted the base/number-input-reducer branch February 5, 2024 06:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: number field This is the name of the generic UI component, not the React module! package: base-ui Specific to @mui/base
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[useNumberInput] Warn when switching between controlled and uncontrolled state
4 participants